From 890dddabbd3346b06af2bcfa8d2402ead6bd00a9 Mon Sep 17 00:00:00 2001 From: Toshihiro Shimizu Date: Mar 18 2016 17:57:51 +0000 Subject: first commit --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acd46bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +Debug +Debug64 +Release +Release64 +GeneratedFiles +.vs +*.opensdf +*.sdf +*.suo +*.user +build* +lzo-2.09 +Lz4_131 +libusb-1.0.9 +glew-1.9.0 +jpeg-9 +tiff-4.0.3 +.clang_complete diff --git a/plugins/blur/CMakeLists.txt b/plugins/blur/CMakeLists.txt new file mode 100644 index 0000000..e87ed2e --- /dev/null +++ b/plugins/blur/CMakeLists.txt @@ -0,0 +1,20 @@ +project(blur_plugin) + +set(HEADERS + ../../toonz/sources/toonzqt/toonz_plugin.h + ../../toonz/sources/toonzqt/toonz_hostif.h) + +set(SOURCES + blur.cpp) + +if (APPLE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") +endif (APPLE) + +add_library(blur SHARED ${HEADERS} ${SOURCES}) + +set_target_properties(blur PROPERTIES + PREFIX "" + SUFFIX ".plugin") + +include_directories(../../toonz/sources/toonzqt ../) diff --git a/plugins/blur/blur.cpp b/plugins/blur/blur.cpp new file mode 100644 index 0000000..6c04230 --- /dev/null +++ b/plugins/blur/blur.cpp @@ -0,0 +1,512 @@ +#include +#include +#include +#include +#include +#include +#include "pixelop.hpp" +#include "toonz_plugin_helper_rect.h" + +#include +#include + +#ifdef _MSC_VER +// Visual Studio is not supported __restrict for reference +#define RESTRICT +#else +#define RESTRICT __restrict +#endif + +extern "C" { +TOONZ_EXPORT int toonz_plugin_init(toonz::host_interface_t *hostif); +TOONZ_EXPORT void toonz_plugin_exit(); +} + +toonz::host_interface_t *ifactory_ = NULL; + +int toonz_plugin_init(toonz::host_interface_t *hostif) +{ + printf("toonz_plugin_init()\n"); + ifactory_ = hostif; + return TOONZ_OK; +} + +void toonz_plugin_exit() +{ + printf("toonz_plugin_exit()\n"); +} + +ToonzRect conv2rect(toonz::rect_t &&r) +{ + return ToonzRect(r.x0, r.y0, r.x1, r.y1); +} + +toonz::rect_t conv2rect(ToonzRect &&r) +{ + return toonz::rect_t{r.x0, r.y0, r.x1, r.y1}; +} + +ToonzRect conv2rect(const toonz::rect_t &r) +{ + return ToonzRect(r.x0, r.y0, r.x1, r.y1); +} + +toonz::rect_t conv2rect(const ToonzRect &r) +{ + return toonz::rect_t{r.x0, r.y0, r.x1, r.y1}; +} + +template +void dump_value(const T &v) +{ + printf("value: type:%s {%d}\n", typeid(T).name(), v); +} + +template <> +void dump_value(const toonz_param_traits_double_t::iovaluetype &v) +{ + printf("value: type:%s {%g}\n", typeid(toonz_param_traits_double_t::iovaluetype).name(), v); +} + +template <> +void dump_value(const toonz_param_traits_range_t::iovaluetype &v) +{ + printf("value: type:%s {%g, %g}\n", typeid(toonz_param_traits_range_t::iovaluetype).name(), v.a, v.b); +} + +template <> +void dump_value(const toonz_param_traits_point_t::iovaluetype &v) +{ + printf("value: type:%s {%g, %g}\n", typeid(toonz_param_traits_point_t::iovaluetype).name(), v.x, v.y); +} + +template <> +void dump_value(const toonz_param_traits_color_t::iovaluetype &v) +{ + printf("value: type:%s {%d, %d, %d, %d}\n", typeid(toonz_param_traits_color_t::iovaluetype).name(), v.c0, v.c1, v.c2, v.m); +} + +template <> +void dump_value(const toonz_param_traits_spectrum_t::iovaluetype &v) +{ + printf("value: type:%s {%g, %g, %g, %g, %g}\n", typeid(toonz_param_traits_spectrum_t::iovaluetype).name(), v.w, v.c0, v.c1, v.c2, v.m); +} + +template <> +void dump_value(const toonz_param_traits_tonecurve_t::iovaluetype &v) +{ + printf("value: type:%s {%g, %g, %d, %d}\n", typeid(toonz_param_traits_tonecurve_t::iovaluetype).name(), v.x, v.y, v.channel, v.interp); +} + +template +bool get_and_check(toonz::node_handle_t node, const char *nm, double frame, double opt = 0) +{ + auto nodeif = grab_interf(TOONZ_UUID_NODE); + auto pif = grab_interf(TOONZ_UUID_PARAM); + toonz_param_handle_t p1; + int ret; + ret = nodeif->get_param(node, nm, &p1); + printf("get_param(%s): ret:%d\n", nm, ret); + + int type = 0; + int cnt = 0; + pif->get_type(p1, frame, &type, &cnt); + printf("parameter(ret:%d): name:%s (type:%d count:%d) expect:(type:%d) typesize:%ld\n", ret, nm, type, cnt, T::E, sizeof(typename T::iovaluetype)); + if (type != T::E) + return false; + + typename T::iovaluetype v; + ret = pif->get_value(p1, frame, &cnt, &v); + printf("get_value: ret:%d count:%d\n", ret, cnt); + dump_value(v); + return true; +} + +template <> +bool get_and_check(toonz::node_handle_t node, const char *nm, double frame, double opt) +{ + auto nodeif = grab_interf(TOONZ_UUID_NODE); + auto pif = grab_interf(TOONZ_UUID_PARAM); + toonz_param_handle_t p1; + int ret; + ret = nodeif->get_param(node, nm, &p1); + printf("get_param(%s): ret:%d\n", nm, ret); + + int type = 0; + int cnt = 0; + ret = pif->get_type(p1, frame, &type, &cnt); + printf("parameter(ret:%d): name:%s (type:%d count:%d) expect:(type:%d) typesize:-\n", ret, nm, type, cnt, toonz_param_traits_string_t::E); + if (type != toonz_param_traits_string_t::E) + return false; + + std::vector v(cnt); + ret = pif->get_value(p1, frame, &cnt, v.data()); + printf("get_value: ret:%d count:%d\n", ret, cnt); + //dump_value< toonz_param_traits_string_t::iovaluetype* >(v); + printf("value: type:%s {%s}\n", typeid(toonz_param_traits_string_t::iovaluetype).name(), v.begin()); + return true; +} + +template <> +bool get_and_check(toonz::node_handle_t node, const char *nm, double frame, double opt) +{ + auto nodeif = grab_interf(TOONZ_UUID_NODE); + auto pif = grab_interf(TOONZ_UUID_PARAM); + toonz_param_handle_t p1; + int ret; + ret = nodeif->get_param(node, nm, &p1); + printf("get_param(%s): ret:%d\n", nm, ret); + int type = 0; + int cnt = 0; + ret = pif->get_type(p1, frame, &type, &cnt); + printf("parameter(ret:%d): name:%s (type:%d count:%d) expect:(type:%d) typesize:-\n", ret, nm, type, cnt, toonz_param_traits_spectrum_t::E); + if (type != toonz_param_traits_spectrum_t::E) + return false; + + toonz_param_traits_spectrum_t::iovaluetype v; + v.w = opt; + ret = pif->get_value(p1, frame, &cnt, &v); + printf("get_value: ret:%d count:%d\n", ret, cnt); + dump_value(v); + return true; +} + +template <> +bool get_and_check(toonz::node_handle_t node, const char *nm, double frame, double opt) +{ + auto nodeif = grab_interf(TOONZ_UUID_NODE); + auto pif = grab_interf(TOONZ_UUID_PARAM); + toonz_param_handle_t p1; + int ret; + ret = nodeif->get_param(node, nm, &p1); + printf("get_param(%s): ret:%d\n", nm, ret); + + int type = 0; + int cnt = 0; + ret = pif->get_type(p1, frame, &type, &cnt); + printf("parameter)(ret:%d): name:%s (type:%d count:%d) expect:(type:%d) sz:-\n", ret, nm, type, cnt, toonz_param_traits_tonecurve_t::E); + if (type != toonz_param_traits_tonecurve_t::E) + return false; + + std::vector v(cnt); + ret = pif->get_value(p1, frame, &cnt, v.data()); + printf("get_value: ret:%d count:%d\n", ret, cnt); + for (int i = 0; i < cnt; i++) { + dump_value(v[i]); + } + return true; +} + +void do_compute(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::tile_handle_t tile) +{ + printf("do_compute(): node:%p tile:%p frame:%g\n", node, tile, frame); + + toonz::port_handle_t port = nullptr; + { + auto nodeif = grab_interf(TOONZ_UUID_NODE); + if (nodeif) { + int ret = nodeif->get_input_port(node, "IPort", &port); + if (ret) + printf("do_compute(): get_input_port: ret:%d\n", ret); + + // test + get_and_check(node, "first", frame); + get_and_check(node, "second", frame); + get_and_check(node, "third", frame); + get_and_check(node, "fourth", frame); + get_and_check(node, "fifth", frame); + get_and_check(node, "sixth", frame); + get_and_check(node, "seventh", frame); + get_and_check(node, "eighth", frame); + get_and_check(node, "ninth", frame, 0.5); + get_and_check(node, "tenth", frame); + + get_and_check(node, "unknown", frame); + + toonz::param_handle_t str; + nodeif->get_param(node, "eighth", &str); + toonz::param_handle_t notstr; + nodeif->get_param(node, "seventh", ¬str); + toonz::param_handle_t spec; + nodeif->get_param(node, "ninth", &spec); + auto pif = grab_interf(TOONZ_UUID_PARAM); + if (pif) { + int t, l; + pif->get_type(str, frame, &t, &l); + int sz; + std::vector shortage_buf(l / 2 + 1); + int ret = pif->get_string_value(str, &sz, l / 2 + 1, shortage_buf.data()); + printf("get_string_value(string): {%s} (length:%d) ret:%d\n", shortage_buf.data(), sz, ret); + + ret = pif->get_string_value(notstr, &sz, l / 2 + 1, shortage_buf.data()); // must be error + printf("get_string_value(not string): sz:%d ret:%d\n", sz, ret); // size must be not changed + + pif->get_type(spec, frame, &t, &l); + toonz_param_spectrum_t spc; + ret = pif->get_spectrum_value(spec, frame, 0, &spc); + printf("get_spectrum_value(spec): ret:%d\n", ret); + dump_value(spc); + ret = pif->get_spectrum_value(str, frame, 0, &spc); + printf("get_spectrum_value(str): ret:%d\n", ret); // must be error + } + } + } + + int blur = 1; + + auto tileif = grab_interf(TOONZ_UUID_TILE); + auto portif = grab_interf(TOONZ_UUID_PORT); + if (port && tileif && portif) { + toonz::fxnode_handle_t fx; + portif->get_fx(port, &fx); + auto fxif = grab_interf(TOONZ_UUID_FXNODE); + if (fxif) { + /* evaluate upstream's flow */ + toonz::rect_t rect; + tileif->get_rectangle(tile, &rect); + + toonz::rect_t inRect = conv2rect(conv2rect(rect).enlarge(blur, blur)); + toonz::tile_handle_t result = nullptr; + tileif->create(&result); + fxif->compute_to_tile(fx, rs, frame, &inRect, tile, result); + + /* evaluate the fx */ + int x = rect.x0; + int y = rect.y0; + int lx = rect.x1 - rect.x0; + int ly = rect.y1 - rect.y0; + + int sstride, dstride; + tileif->get_raw_stride(result, &sstride); + tileif->get_raw_stride(tile, &dstride); + int selement_type, delement_type; + + tileif->get_element_type(result, &delement_type); + tileif->get_element_type(tile, &selement_type); + uint8_t *saddr, *daddr; + + tileif->get_raw_address_unsafe(result, reinterpret_cast(&saddr)); + tileif->get_raw_address_unsafe(tile, reinterpret_cast(&daddr)); + + printf("%f %f %f %f\n", rect.x0, rect.y0, rect.x1, rect.y1); + + printf("-+ x:%d y:%d lx:%d ly:%d\n", x, y, lx, ly); + printf(" + src tile: addr:%p stride:%d type:%d\n", saddr, sstride, selement_type); + printf(" + dst tile: addr:%p stride:%d type:%d\n", daddr, dstride, delement_type); + + // need compatibility check for formats + + hv_kernel(daddr, saddr + sstride + 4, lx, ly, dstride, sstride, [](uint8_t v[4], const uint8_t *p, int stride, int xinbytes, int yinbytes) { + uint32_t l00 = *reinterpret_cast(p + -stride - 4); + uint32_t l10 = *reinterpret_cast(p + -stride); + uint32_t l20 = *reinterpret_cast(p + -stride + 4); + uint32_t l01 = *reinterpret_cast(p - 4); + uint32_t l11 = *reinterpret_cast(p); + uint32_t l21 = *reinterpret_cast(p + 4); + uint32_t l02 = *reinterpret_cast(p + stride - 4); + uint32_t l12 = *reinterpret_cast(p + stride); + uint32_t l22 = *reinterpret_cast(p + stride + 4); + uint32_t r = 0, g = 0, b = 0, m = 0; + //printf("col:0x%08x [%d, %d]\n", l11, xinbytes, yinbytes); + + /* TODO: + toonz の TPixel は char r, g, b, m; のように持っているのでそのまま uin32_t だとエンディアンが変わる. + toonz ビルド時の configuration はコンパイル時に参照できるようにしておくべきだ. + */ + + m += ((l00 >> 24) & 0xff); + m += ((l10 >> 24) & 0xff); + m += ((l20 >> 24) & 0xff); + m += ((l01 >> 24) & 0xff); + m += ((l11 >> 24) & 0xff); + m += ((l21 >> 24) & 0xff); + m += ((l02 >> 24) & 0xff); + m += ((l12 >> 24) & 0xff); + m += ((l22 >> 24) & 0xff); + + b += ((l00 >> 16) & 0xff); + b += ((l10 >> 16) & 0xff); + b += ((l20 >> 16) & 0xff); + b += ((l01 >> 16) & 0xff); + b += ((l11 >> 16) & 0xff); + b += ((l21 >> 16) & 0xff); + b += ((l02 >> 16) & 0xff); + b += ((l12 >> 16) & 0xff); + b += ((l22 >> 16) & 0xff); + + g += ((l00 >> 8) & 0xff); + g += ((l10 >> 8) & 0xff); + g += ((l20 >> 8) & 0xff); + g += ((l01 >> 8) & 0xff); + g += ((l11 >> 8) & 0xff); + g += ((l21 >> 8) & 0xff); + g += ((l02 >> 8) & 0xff); + g += ((l12 >> 8) & 0xff); + g += ((l22 >> 8) & 0xff); + + r += ((l00 >> 0) & 0xff); + r += ((l10 >> 0) & 0xff); + r += ((l20 >> 0) & 0xff); + r += ((l01 >> 0) & 0xff); + r += ((l11 >> 0) & 0xff); + r += ((l21 >> 0) & 0xff); + r += ((l02 >> 0) & 0xff); + r += ((l12 >> 0) & 0xff); + r += ((l22 >> 0) & 0xff); + + v[0] = static_cast(r / 9); + v[1] = static_cast(g / 9); + v[2] = static_cast(b / 9); + v[3] = static_cast(m / 9); + }); + } + } +} + +int do_get_bbox(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::rect_t *rect) +{ + printf("do_get_bbox(): node:%p\n", node); + bool modified = false; + + toonz::port_handle_t port = nullptr; + { + auto nodeif = grab_interf(TOONZ_UUID_NODE); + if (nodeif) { + int ret = nodeif->get_input_port(node, "IPort", &port); + printf("do_get_bbox(): get_input_port:%d\n", ret); + } + } + + auto portif = grab_interf(TOONZ_UUID_PORT); + if (port && portif) { + int con = 0; + portif->is_connected(port, &con); + if (con) { + toonz::fxnode_handle_t fx; + portif->get_fx(port, &fx); + auto fxif = grab_interf(TOONZ_UUID_FXNODE); + if (fxif) { + int ret = 0; + /* この引数の順番つらいなぁ, fx, rs, frame, rect の順だよね */ + fxif->get_bbox(fx, rs, frame, rect, &ret); + + /* 画素単位の値を現在の座標系に合わせる */ + toonz::affine_t a = rs->affine; + double det = a.a11 * a.a22 - a.a12 * a.a21; + double scale = sqrt(fabs(det)); + double blur = 1.0 / scale; + + ToonzRect r = conv2rect(*rect); + r = r.enlarge(blur, blur); + *rect = conv2rect(std::move(r)); + modified = true; + } + } + } + return modified; +} + +param_desc_t params0_[] = { + param_desc_ctor("first", "1st", {1.0 /* default */, -1.0 /* min */, 1.0 /* max */}, "description of the first param"), + param_desc_ctor("second", "2nd", {{48.0, 64.0} /* default */, {0.0, 180.0} /* min-max */}), + param_desc_ctor("third", "3rd", {0xff, 0xff, 0xff, 0xff} /* white */), + param_desc_ctor("fourth", "4th", {{64, -64} /* default */, {-200, 200} /* min */, {-200, +200} /* max */}), +}; + +const char *strtbl_[] = {"tokyo", "osaka", "nagoya"}; + +param_desc_t params1_[] = { + param_desc_ctor("fifth", "5th", {2, 3, strtbl_}), + param_desc_ctor("sixth", "6th", {2, -10, 10}, "this is integer"), + param_desc_ctor("seventh", "7th", {1}), +}; + +toonz_param_traits_spectrum_t::valuetype points_[] = {{0.0, 1.0, 0.0, 0.0, 1.0}, + {0.5, 0.5, 0.0, 0.5, 1.0}, + {1.0, 0.0, 0.0, 0.5, 1.0}}; + +param_desc_t params2_[] = { + param_desc_ctor("eighth", "8th", {"this is sample message"}), + param_desc_ctor("ninth", "9th", {0.1, 3, points_}), + param_desc_ctor("tenth", "10th", {/* tonecurve has no default */}), +}; + +toonz_param_group_t groups0_[] = { + param_group_ctor("Group1", sizeof(params0_) / sizeof(param_desc_t), params0_), + param_group_ctor("Group2", sizeof(params1_) / sizeof(param_desc_t), params1_), + param_group_ctor("Group3", sizeof(params2_) / sizeof(param_desc_t), params2_), +}; + +toonz_param_page_t pages_[] = { + param_page_ctor("Properties", sizeof(groups0_) / sizeof(toonz_param_group_t), groups0_), +}; + +int node_setup(toonz::node_handle_t node) +{ + printf("blur: setup(): node:%p\n", node); + + auto setup = grab_interf(TOONZ_UUID_SETUP); + if (setup) { + int errcode = 0; + void *entry = NULL; + int ret = setup->set_parameter_pages_with_error(node, sizeof(pages_) / sizeof(toonz_param_page_t), pages_, &errcode, &entry); + if (ret) { + printf("setup error:0x%x reason:0x%x entry:%p\n", ret, errcode, entry); + } + setup->add_input_port(node, "IPort", TOONZ_PORT_TYPE_RASTER); + } + return 0; +} + +int node_create(toonz::node_handle_t node) +{ + printf("blur: create(): node:%p\n", node); + return TOONZ_OK; +} + +int node_destroy(toonz::node_handle_t node) +{ + printf("destroy():node:%p\n", node); + return TOONZ_OK; +} + +int can_handle(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); + return TOONZ_OK; +} + +size_t get_memory_requirement(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame, const toonz_rect_t *rect) +{ + printf("%s:\n", __FUNCTION__); + return TOONZ_OK; +} + +void on_new_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +void on_end_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +toonz_nodal_rasterfx_handler_t_ toonz_plugin_node_handler = + { + {1, 0}, // ver + do_compute, // do_compute + do_get_bbox, // do_get_bbox + can_handle, // can_handle + get_memory_requirement, // get_memory_requirement + on_new_frame, // on_new_frame + on_end_frame, // on_end_frame + node_create, // create + node_destroy, // destroy + node_setup}; + +extern "C" { +TOONZ_PLUGIN_PROBE_BEGIN(TOONZ_IF_VER(1, 0)) +TOONZ_PLUGIN_PROBE_DEFINE(TOONZ_PLUGIN_VER(1, 0), "blur-plugin" /* name */, "dwango" /* vendor */, "libblur.plugin" /* identifier */, "a blur plugin for test", "http://dwango.co.jp/", TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB, &toonz_plugin_node_handler) +TOONZ_PLUGIN_PROBE_END; +} diff --git a/plugins/blur/pixelop.hpp b/plugins/blur/pixelop.hpp new file mode 100644 index 0000000..fba0405 --- /dev/null +++ b/plugins/blur/pixelop.hpp @@ -0,0 +1,75 @@ +#if !defined(PIXEL_OP_HPP__) +#define PIXEL_OP_HPP__ + +#include + +template +void hv_op(T *__restrict ptr, const T *__restrict src, int w, int h, int dst_stride, int src_stride, std::function &&f) +{ + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + T c0 = src[j * component]; + T c1 = src[j * component + 1]; + T c2 = src[j * component + 2]; + ptr[j] = f(std::move(c0), std::move(c1), std::move(c2)); + } + src += src_stride; + ptr += dst_stride; + } +} + +template +void hv_op(const T *__restrict src, int w, int h, int stride, std::function &&f) +{ + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + T c0 = src[j * component]; + T c1 = src[j * component + 1]; + T c2 = src[j * component + 2]; + f(std::move(c0), std::move(c1), std::move(c2), j, i); + } + src += stride; + } +} + +template +void hv_op(T *__restrict ptr, const T *__restrict src, int w, int h, int stride, std::function &&f) +{ + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + T c0 = src[j]; + ptr[j] = f(std::move(c0), j, i); + } + src += stride; + ptr += stride; + } +} + +template +void hv_kernel(T *__restrict ptr, const T *__restrict src, int w, int h, int stride, std::function &&f) +{ + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + ptr[j] = f(src + j, stride, j, i); + } + src += stride; + ptr += stride; + } +} + +template +void hv_kernel(T *__restrict ptr, const T *__restrict src, int w, int h, int dstride, int sstride, std::function &&f) +{ + for (int i = 0; i < h * components; i += components) { + for (int j = 0; j < w * components; j += components) { + T channels[components]; + f(channels, src + j, sstride, j, i); + for (int k = 0; k < components; k++) + ptr[j + k] = channels[k]; + } + src += sstride; + ptr += dstride; + } +} + +#endif diff --git a/plugins/geom/CMakeLists.txt b/plugins/geom/CMakeLists.txt new file mode 100644 index 0000000..b907954 --- /dev/null +++ b/plugins/geom/CMakeLists.txt @@ -0,0 +1,20 @@ +project(geom_plugin) + +set(HEADERS + ../../toonz/sources/toonzqt/toonz_plugin.h + ../../toonz/sources/toonzqt/toonz_hostif.h) + +set(SOURCES + geom.cpp) + +if (APPLE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") +endif (APPLE) + +add_library(geom SHARED ${HEADERS} ${SOURCES}) + +set_target_properties(geom PROPERTIES + PREFIX "" + SUFFIX ".plugin") + +include_directories(../../toonz/sources/toonzqt ../) diff --git a/plugins/geom/geom.cpp b/plugins/geom/geom.cpp new file mode 100644 index 0000000..2f41009 --- /dev/null +++ b/plugins/geom/geom.cpp @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include "toonz_plugin_helper_rect.h" +#include "toonz_plugin_helper_affine.h" + +#include +#include + +#ifdef _MSC_VER +#define RESTRICT +#else +#define RESTRICT __restrict +#endif + +extern "C" { +TOONZ_EXPORT int toonz_plugin_init(toonz::host_interface_t *hostif); +TOONZ_EXPORT void toonz_plugin_exit(); +} + +toonz::host_interface_t *ifactory_ = NULL; + +int toonz_plugin_init(toonz::host_interface_t *hostif) +{ + printf("toonz_plugin_init()\n"); + ifactory_ = hostif; + return TOONZ_OK; +} + +void toonz_plugin_exit() +{ + printf("toonz_plugin_exit()\n"); +} + +void do_compute(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::tile_handle_t tile) +{ + printf("do_compute(): node:%p tile:%p frame:%g\n", node, tile, frame); + + auto nodeif = grab_interf(TOONZ_UUID_NODE); + if (nodeif) { + void *param = NULL; + int r = nodeif->get_param(node, "p3rd", ¶m); + if (r == TOONZ_OK) { + printf("do_compute: get param: p:%p\n", param); + auto pif = grab_interf(TOONZ_UUID_PARAM); + if (pif) { + int type, counts; + pif->get_type(param, frame, &type, &counts); + printf("do_compute: get value type:%d\n", type); + toonz_param_traits_point_t::iovaluetype v; + pif->get_value(param, frame, &counts, &v); + + printf("do_compute: value:[%g, %g]\n", v.x, v.y); + } + } else { + printf("do_compute: error to get param:0x%x\n", r); + } + } + + auto tileif = grab_interf(TOONZ_UUID_TILE); + if (tileif) { + toonz::rect_t rect; + tileif->get_rectangle(tile, &rect); + + /* evaluate the fx */ + int lx = rect.x1 - rect.x0; + int ly = rect.y1 - rect.y0; + + ToonzAffine mat(rs->affine); + ToonzAffine invmat = mat.inv(); + ToonzPoint p = invmat * ToonzPoint(rect.x0, rect.y0); + + int dstride = 0; + tileif->get_raw_stride(tile, &dstride); + int delement_type = 0; + tileif->get_element_type(tile, &delement_type); + uint8_t *daddr = NULL; + tileif->get_raw_address_unsafe(tile, reinterpret_cast(&daddr)); + + printf("-+ lx:%d ly:%d\n", lx, ly); + printf("-+ px:%g py:%g\n", p.x, p.y); + printf(" + dst rect(%g, %g, %g, %g)\n", rect.x0, rect.y0, rect.x1, rect.y1); + printf(" + dst tile: addr:%p stride:%d type:%d\n", daddr, dstride, delement_type); + + // need compatibility check for formats + for (int i = 0; i < ly; i++) { + uint32_t *ptr = reinterpret_cast(daddr + i * dstride); + /* [rx, ry] は effect の面内を描くベクトル. 半径 r 円の中心が [0, 0] で、 [-lx/2, -ly/2]-[lx/2, ly/2] + [j, i] は dst のスキャンラインのベクトルで [0, 0]-[lx, ly] */ + double rx = p.x; + double ry = p.y; + for (int j = 0; j < lx; j++) { + double r = std::sqrt(rx * rx + ry * ry); + double k = 0; + if (r < 64) + k = 1; + uint8_t c = (uint8_t)(k * 255); + *(ptr + j) = 0xff000000 | (c << 16) | (c << 8) | c; + /* hor advance */ + rx += invmat.a11; + ry += invmat.a21; + } + /* vert advance */ + p.x += invmat.a12; + p.y += invmat.a22; + } + } +} + +int do_get_bbox(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::rect_t *rect) +{ + printf("do_get_bbox(): node:%p\n", node); + bool modified = false; +/* 円の周辺の黒いマスク部分が bbox に相当する: + コンサバには無限に大きな矩形を返すのが最もコンサバな方法だが、 rendering_setting_t の affine 変換行列を用いてよりよい予測をすることもできる. + */ +#if 0 + /* too concervative */ + rect->x0 = -std::numeric_limits< double >::max(); + rect->y0 = -std::numeric_limits< double >::max(); + rect->x1 = std::numeric_limits< double >::max(); + rect->y1 = std::numeric_limits< double >::max(); +#endif +#if 0 + /* too aggressive */ + rect->x0 = -64; + rect->y0 = -64; + rect->x1 = 64; + rect->y1 = 64; +#endif + /* more reasonable than above */ + ToonzRect r(-64, -64, 64, 64); + ToonzRect bbox = ToonzAffine(rs->affine) * r; + rect->x0 = bbox.x0; + rect->y0 = bbox.y0; + rect->x1 = bbox.x1; + rect->y1 = bbox.y1; + return modified; +} + +param_desc_t params0_[] = { + param_desc_ctor("p1st", "", {1.0 /* default */, -1.0 /* min */, 1.0 /* max */}), + param_desc_ctor("p2nd", "", {{48.0, 64.0} /* default */, {0.0, 180.0} /* min-max */}), + param_desc_ctor("p3rd", "", {{0, 0} /* default */, {-10, 10} /* min */, {-200, +200} /* max */}), +}; + +param_desc_t params1_[] = { + param_desc_ctor("g1st", "", {1.0 /* default */, -1.0 /* min */, 1.0 /* max */}), + param_desc_ctor("g2nd", "", {{48.0, 64.0} /* default */, {0.0, 180.0} /* min-max */}), + param_desc_ctor("g3rd", "", {{1, 1} /* default */, {-10, 10} /* min */, {-200, +200} /* max */}), +}; + +param_desc_t params2_[] = { + param_desc_ctor("pp1st", "", {1.0 /* default */, -1.0 /* min */, 1.0 /* max */}), + param_desc_ctor("pp2nd", "", {{48.0, 64.0} /* default */, {0.0, 90.0} /* min-max */}), + param_desc_ctor("pp3rd", "", {{1, 1} /* default */, {-10, 10} /* min */, {-200, +200} /* max */}), +}; + +toonz_param_group_t groups0_[] = { + param_group_ctor("First Group", sizeof(params0_) / sizeof(param_desc_t), params0_), + param_group_ctor("Second Group", sizeof(params1_) / sizeof(param_desc_t), params1_), +}; + +toonz_param_group_t groups1_[] = { + param_group_ctor("Third Group", sizeof(params2_) / sizeof(param_desc_t), params2_), +}; + +toonz_param_page_t pages_[] = { + param_page_ctor("Main Page", sizeof(groups0_) / sizeof(toonz_param_group_t), groups0_), + param_page_ctor("Sub Page", sizeof(groups1_) / sizeof(toonz_param_group_t), groups1_), +}; + +int node_setup(toonz::node_handle_t node) +{ + auto setup = grab_interf(TOONZ_UUID_SETUP); + if (setup) { + int errcode = 0; + void *entry = NULL; + int ret = setup->set_parameter_pages_with_error(node, 2, pages_, &errcode, &entry); + if (ret) { + printf("setup error:0x%x reason:0x%x entry:%p\n", ret, errcode, entry); + } + + setup->add_input_port(node, "IPort", TOONZ_PORT_TYPE_RASTER); + } + return 0; +} + +int node_create(toonz::node_handle_t node) +{ + printf("create(): node:%p\n", node); + return TOONZ_OK; +} + +int node_destroy(toonz::node_handle_t node) +{ + printf("destroy():node:%p\n", node); + return TOONZ_OK; +} + +int can_handle(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); + /* geometric plugin のように Bounding Box が自明の場合、 Skew などの変換の影響でフルスクリーン効果のような大きな Bouding Box が + 過剰に大きくなってしまうことがある. これを防ぐには非ゼロを返すこと. + */ + return 1; /* non zero is 'true' */ +} + +size_t get_memory_requirement(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame, const toonz_rect_t *rect) +{ + printf("%s:\n", __FUNCTION__); + return TOONZ_OK; +} + +void on_new_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +void on_end_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +toonz_nodal_rasterfx_handler_t_ toonz_plugin_node_handler = + { + {1, 0}, // ver + do_compute, // do_compute + do_get_bbox, // do_get_bbox + can_handle, // can_handle + get_memory_requirement, // get_memory_requirement + on_new_frame, // on_new_frame + on_end_frame, // on_end_frame + node_create, // create + node_destroy, // destroy + node_setup}; + +extern "C" { +TOONZ_PLUGIN_PROBE_BEGIN(TOONZ_IF_VER(1, 0)) +TOONZ_PLUGIN_PROBE_DEFINE(TOONZ_PLUGIN_VER(1, 0), "geom-plugin" /* name */, "dwango" /* vendor */, "geom.plugin" /* identifier */, "a geom plugin for test", "http://dwango.co.jp/", TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB | TOONZ_PLUGIN_CLASS_MODIFIER_GEOMETRIC, &toonz_plugin_node_handler) +TOONZ_PLUGIN_PROBE_END; +} diff --git a/plugins/multiplugin/CMakeLists.txt b/plugins/multiplugin/CMakeLists.txt new file mode 100644 index 0000000..2de78bf --- /dev/null +++ b/plugins/multiplugin/CMakeLists.txt @@ -0,0 +1,20 @@ +project(multi_plugin) + +set(HEADERS + ../../toonz/sources/toonzqt/toonz_plugin.h + ../../toonz/sources/toonzqt/toonz_hostif.h) + +set(SOURCES + multi.cpp) + +if (APPLE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") +endif (APPLE) + +add_library(multi SHARED ${HEADERS} ${SOURCES}) + +set_target_properties(multi PROPERTIES + PREFIX "" + SUFFIX ".plugin") + +include_directories(../../toonz/sources/toonzqt ../) diff --git a/plugins/multiplugin/multi.cpp b/plugins/multiplugin/multi.cpp new file mode 100644 index 0000000..c369a15 --- /dev/null +++ b/plugins/multiplugin/multi.cpp @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include +#include +#include "toonz_plugin_helper_rect.h" +#include "toonz_plugin_helper_affine.h" +#include +#include + +#ifdef _MSC_VER +// Visual Studio is not supported __restrict for reference +#define RESTRICT +#else +#define RESTRICT __restrict +#endif + +extern "C" { +TOONZ_EXPORT void toonz_plugin_probe(toonz_plugin_probe_t **begin, toonz_plugin_probe_t **end); +TOONZ_EXPORT int toonz_plugin_init(toonz::host_interface_t *hostif); +TOONZ_EXPORT void toonz_plugin_exit(); +} + +toonz::host_interface_t *ifactory_ = NULL; + +int toonz_plugin_init(toonz::host_interface_t *hostif) +{ + printf("toonz_plugin_init()\n"); + ifactory_ = hostif; + return TOONZ_OK; +} + +void toonz_plugin_exit() +{ + printf("toonz_plugin_exit()\n"); +} + +void do_compute(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::tile_handle_t tile) +{ + printf("do_compute(): node:%p tile:%p frame:%g\n", node, tile, frame); + + auto tileif = grab_interf(TOONZ_UUID_TILE); + if (tileif) { + toonz::rect_t rect; + tileif->get_rectangle(tile, &rect); + + /* evaluate the fx */ + int lx = rect.x1 - rect.x0; + int ly = rect.y1 - rect.y0; + + ToonzAffine mat(rs->affine); + ToonzAffine invmat = mat.inv(); + ToonzPoint p = invmat * ToonzPoint(rect.x0, rect.y0); + + int dstride = 0; + tileif->get_raw_stride(tile, &dstride); + int delement_type = 0; + tileif->get_element_type(tile, &delement_type); + uint8_t *daddr = NULL; + tileif->get_raw_address_unsafe(tile, reinterpret_cast(&daddr)); + + printf("-+ lx:%d ly:%d\n", lx, ly); + printf("-+ px:%g py:%g\n", p.x, p.y); + printf(" + dst rect(%g, %g, %g, %g)\n", rect.x0, rect.y0, rect.x1, rect.y1); + printf(" + dst tile: addr:%p stride:%d type:%d\n", daddr, dstride, delement_type); + + // need compatibility check for formats + for (int i = 0; i < ly; i++) { + uint32_t *ptr = reinterpret_cast(daddr + i * dstride); + /* [rx, ry] は effect の面内を描くベクトル. 半径 r 円の中心が [0, 0] で、 [-lx/2, -ly/2]-[lx/2, ly/2] + [j, i] は dst のスキャンラインのベクトルで [0, 0]-[lx, ly] */ + double rx = p.x; + double ry = p.y; + for (int j = 0; j < lx; j++) { + double r = std::sqrt(rx * rx + ry * ry); + double k = 0; + if (r < 64) + k = 1; + uint8_t c = (uint8_t)(k * 255); + *(ptr + j) = 0xff000000 | (c << 16) | (c << 8) | c; + /* hor advance */ + rx += invmat.a11; + ry += invmat.a21; + } + /* vert advance */ + p.x += invmat.a12; + p.y += invmat.a22; + } + } +} + +int do_get_bbox(toonz::node_handle_t node, const toonz::rendering_setting_t *rs, double frame, toonz::rect_t *rect) +{ + printf("do_get_bbox(): node:%p\n", node); + bool modified = false; +/* 円の周辺の黒いマスク部分が bbox に相当する: + コンサバには無限に大きな矩形を返すのが最もコンサバな方法だが、 rendering_setting_t の affine 変換行列を用いてよりよい予測をすることもできる. + */ +#if 0 + /* too concervative */ + rect->x0 = -std::numeric_limits< double >::max(); + rect->y0 = -std::numeric_limits< double >::max(); + rect->x1 = std::numeric_limits< double >::max(); + rect->y1 = std::numeric_limits< double >::max(); +#endif +#if 0 + /* too aggressive */ + rect->x0 = -64; + rect->y0 = -64; + rect->x1 = 64; + rect->y1 = 64; +#endif + /* more reasonable than above */ + ToonzRect r(-64, -64, 64, 64); + ToonzRect bbox = ToonzAffine(rs->affine) * r; + rect->x0 = bbox.x0; + rect->y0 = bbox.y0; + rect->x1 = bbox.x1; + rect->y1 = bbox.y1; + return modified; +} + +const char *strtbl[] = {"tokyo", "osaka", "kobe"}; +param_desc_t params0_[] = { + param_desc_ctor("p1st", "integer", {2 /* default */, -10 /* min */, 10 /* max */}, "this is integer"), + param_desc_ctor("p2nd", "enumuration", {1, 3, strtbl}), + param_desc_ctor("p3rd", "boolean", {1}), + param_desc_ctor("p4th", "string", {"a message"}), +}; + +toonz_param_traits_spectrum_t::valuetype specs[] = {{0.0, 1.0, 0.0, 0.0, 1.0}, + {0.5, 0.5, 0.0, 0.5, 1.0}, + {1.0, 0.0, 0.0, 0.5, 1.0}}; + +param_desc_t params1_[] = { + param_desc_ctor("p1st", "color", {0, 0, 255 /* blue */, 255}), + param_desc_ctor("p2nd", "spectrum", {0.1, 3, specs}), + param_desc_ctor("p3rd", "tone curve", {}), +}; + +toonz_param_group_t groups0_[] = { + param_group_ctor("Group", sizeof(params0_) / sizeof(param_desc_t), params0_), +}; + +toonz_param_group_t groups1_[] = { + param_group_ctor("Group", sizeof(params1_) / sizeof(param_desc_t), params1_), +}; + +toonz_param_page_t pages0_[] = { + param_page_ctor("1st plugin", sizeof(groups0_) / sizeof(toonz_param_group_t), groups0_), +}; + +toonz_param_page_t pages1_[] = { + param_page_ctor("2nd plugin", sizeof(groups1_) / sizeof(toonz_param_group_t), groups1_), +}; + +int node_setup_a(toonz::node_handle_t node) +{ + printf("plugin A: setup(): node:%p\n", node); + auto setup = grab_interf(TOONZ_UUID_SETUP); + if (setup) { + int errcode = 0; + void *position = NULL; + int err = setup->set_parameter_pages_with_error(node, 1, pages0_, &errcode, &position); + if (err == TOONZ_ERROR_INVALID_VALUE) { + printf("setup err: reason:0x%x address:%p\n", errcode, position); + } + } + return 0; +} + +int node_setup_b(toonz::node_handle_t node) +{ + printf("plugin B: setup(): node:%p\n", node); + auto setup = grab_interf(TOONZ_UUID_SETUP); + if (setup) { + int errcode = 0; + void *position = NULL; + int err = setup->set_parameter_pages_with_error(node, 1, pages1_, &errcode, &position); + if (err == TOONZ_ERROR_INVALID_VALUE) { + printf("setup err: reason:0x%x address:%p\n", errcode, position); + } + } + return 0; +} + +int node_create(toonz::node_handle_t node) +{ + printf("create(): node:%p\n", node); + return TOONZ_OK; +} + +int node_destroy(toonz::node_handle_t node) +{ + printf("destroy():node:%p\n", node); + return TOONZ_OK; +} + +int can_handle(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); + /* geometric plugin のように Bounding Box が自明の場合、 Skew などの変換の影響でフルスクリーン効果のような大きな Bouding Box が + 過剰に大きくなってしまうことがある. これを防ぐには非ゼロを返すこと. + */ + return 1; /* non zero is 'true' */ +} + +size_t get_memory_requirement(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame, const toonz_rect_t *rect) +{ + printf("%s:\n", __FUNCTION__); + return TOONZ_OK; +} + +void on_new_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +void on_end_frame(toonz_node_handle_t node, const toonz_rendering_setting_t *rs, double frame) +{ + printf("%s:\n", __FUNCTION__); +} + +toonz_nodal_rasterfx_handler_t_ toonz_plugin_node_handler0 = { + TOONZ_IF_VER(1, 0), // type ver + do_compute, // do_compute + do_get_bbox, // do_get_bbox + can_handle, // can_handle + get_memory_requirement, // get_memory_requirement + on_new_frame, // on_new_frame + on_end_frame, // on_end_frame + node_create, // create + node_destroy, // destroy + node_setup_a}; + +toonz_nodal_rasterfx_handler_t_ toonz_plugin_node_handler1 = { + TOONZ_IF_VER(1, 0), // type ver + do_compute, // do_compute + do_get_bbox, // do_get_bbox + can_handle, // can_handle + get_memory_requirement, // get_memory_requirement + on_new_frame, // on_new_frame + on_end_frame, // on_end_frame + node_create, // create + node_destroy, // destroy + node_setup_b}; + +/** + * Memory + * + * |//////////////////////| + * +----------------------+ <- toonz_plugin_info_list.begin + * | 1st plugin | + * +----------------------+ + * | 2nd plugin | + * +----------------------+ + * | | + * : + * | | + * +----------------------+ <- toonz_plugin_info_list.end + * | empty(all zeroed) | + * +----------------------+ + * |//// unused area /////| + * + * 1. the array of plugins should be lain in the continuous area on a memory. + * 2. an empty element must be zero-ed all (including 'ver'). + * 3. 'ver' fields must be same each other. + */ + +extern "C" { +TOONZ_PLUGIN_PROBE_BEGIN(TOONZ_IF_VER(1, 0)) +TOONZ_PLUGIN_PROBE_DEFINE(TOONZ_PLUGIN_VER(1, 0), "multi1-plugin" /* name */, "dwango" /* vendor */, "multi1.plugin" /* identifier */, "1st plugin from the single module", "http://dwango.co.jp/", TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB | TOONZ_PLUGIN_CLASS_MODIFIER_GEOMETRIC, &toonz_plugin_node_handler0) +, + TOONZ_PLUGIN_PROBE_DEFINE(TOONZ_PLUGIN_VER(1, 0), "multi2-plugin" /* name */, "dwango" /* vendor */, "multi2.plugin" /* identifier */, "2nd plugin from the single module", "http://dwango.co.jp/", TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB | TOONZ_PLUGIN_CLASS_MODIFIER_GEOMETRIC, &toonz_plugin_node_handler1) + TOONZ_PLUGIN_PROBE_END; +} + +const toonz_plugin_probe_list_t_ *toonz_plugin_probe() +{ + printf("toonz_plugin_probe()\n"); + return &toonz_plugin_info_list; +} diff --git a/plugins/utils/interf_holder.hpp b/plugins/utils/interf_holder.hpp new file mode 100644 index 0000000..00eacc0 --- /dev/null +++ b/plugins/utils/interf_holder.hpp @@ -0,0 +1,22 @@ +#if !defined(UTILS_INTERFACEHOLDER_HPP__) +#define UTILS_INTERFACEHOLDER_HPP__ +#include +#include + +extern toonz::host_interface_t *ifactory_; + +void release_interf(void *interf) +{ + ifactory_->release_interface(interf); +} + +template +std::unique_ptr grab_interf(const toonz::UUID *uuid) +{ + T *interf = nullptr; + if (ifactory_->query_interface(uuid, reinterpret_cast(&interf))) + return std::unique_ptr(nullptr, release_interf); + return std::move(std::unique_ptr(interf, release_interf)); +} + +#endif diff --git a/plugins/utils/param_traits.hpp b/plugins/utils/param_traits.hpp new file mode 100644 index 0000000..f24be1e --- /dev/null +++ b/plugins/utils/param_traits.hpp @@ -0,0 +1,81 @@ +#if !defined(UTILS_PARAM_TRAITS_HPP__) +#define UTILS_PARAM_TRAITS_HPP__ + +#include +#include +#include + +/* helper-types to define toonz_param_*_t's table */ +struct param_desc_t : public toonz_param_desc_t { + param_desc_t(const char *k, const char *l, int ttag, const char *nt = "") + { + base = {{1, 0} /* type version */, TOONZ_PARAM_DESC_TYPE_PARAM, l}; + key = k; + note = nt; + reserved_[0] = NULL; // must be zero + reserved_[1] = NULL; + traits_tag = ttag; + } +}; + +template +void init_param_traits_union(toonz_param_desc_t &t, const T &p){}; + +template +struct param_desc_holder_t { + typedef T realtype; + param_desc_t t; + param_desc_holder_t(const char *key, const char *label, const T &p, const char *note) : t{key, label, T::E, note} { init_param_traits_union(t, p); } +}; + +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_double_t &p) { t.traits.d = p; } + +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_int_t &p) { t.traits.i = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_enum_t &p) { t.traits.e = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_range_t &p) { t.traits.rd = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_bool_t &p) { t.traits.b = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_color_t &p) { t.traits.c = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_point_t &p) { t.traits.p = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_spectrum_t &p) { t.traits.g = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_string_t &p) { t.traits.s = p; } +template <> +void init_param_traits_union(toonz_param_desc_t &t, const toonz_param_traits_tonecurve_t &p) { t.traits.t = p; } + +typedef param_desc_holder_t traits_double_t; +typedef param_desc_holder_t traits_int_t; +typedef param_desc_holder_t traits_enum_t; +typedef param_desc_holder_t traits_range_t; +typedef param_desc_holder_t traits_bool_t; +typedef param_desc_holder_t traits_color_t; +typedef param_desc_holder_t traits_point_t; +typedef param_desc_holder_t traits_spectrum_t; +typedef param_desc_holder_t traits_string_t; +typedef param_desc_holder_t traits_tonecurve_t; + +template +static param_desc_t param_desc_ctor(const char *key, const char *label, const typename T::realtype &p, const char *note = "") +{ + param_desc_holder_t t(key, label, p, note); + return t.t; +} + +static toonz_param_group_t param_group_ctor(const char *label, size_t n, toonz_param_desc_t *p) +{ + return {{{1, 0}, TOONZ_PARAM_DESC_TYPE_GROUP, label}, static_cast(n), p}; +} + +static toonz_param_page_t param_page_ctor(const char *label, size_t n, toonz_param_group_t *g) +{ + return {{{1, 0}, TOONZ_PARAM_DESC_TYPE_PAGE, label}, static_cast(n), g}; +} + +#endif diff --git a/stuff/config/batches.xml b/stuff/config/batches.xml new file mode 100644 index 0000000..89435b1 --- /dev/null +++ b/stuff/config/batches.xml @@ -0,0 +1,7 @@ + + + "the Tab 3.0.4" + + + + diff --git a/stuff/config/brush.txt b/stuff/config/brush.txt new file mode 100644 index 0000000..2e7d863 --- /dev/null +++ b/stuff/config/brush.txt @@ -0,0 +1,20 @@ + + + variable_0_15 0 15 10 50 70 + + + constant_10 10 10 10 50 70 + + + constant_15 15 15 10 50 70 + + + airbrush_20_100 20 100 100 20 30 + + + ink_1_15 1 15 70 90 100 + + + pencil_1_10 1 10 70 80 30 + + diff --git a/stuff/config/contrib/GotoBLAS_License.txt b/stuff/config/contrib/GotoBLAS_License.txt new file mode 100644 index 0000000..00e540f --- /dev/null +++ b/stuff/config/contrib/GotoBLAS_License.txt @@ -0,0 +1,34 @@ + GotoBLAS2 (Version 1.13) + ===================== + +Copyright 2009, 2010 The University of Texas at Austin. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF TEXAS AT AUSTIN ``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 UNIVERSITY OF TEXAS AT +AUSTIN 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. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of The University of +Texas at Austin. diff --git a/stuff/config/contrib/OpenBLAS_license.txt b/stuff/config/contrib/OpenBLAS_license.txt new file mode 100644 index 0000000..3870808 --- /dev/null +++ b/stuff/config/contrib/OpenBLAS_license.txt @@ -0,0 +1,31 @@ + OpenBLAS (Version 0.1.1) + ===================== + +Copyright (c) 2011,2012 Lab of Parallel Software and Computational Science,ISCAS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the ISCAS 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. diff --git a/stuff/config/contrib/SuperLU_license.txt b/stuff/config/contrib/SuperLU_license.txt new file mode 100644 index 0000000..103f72b --- /dev/null +++ b/stuff/config/contrib/SuperLU_license.txt @@ -0,0 +1,32 @@ + SuperLU (Version 4.1) + ===================== + +Copyright (c) 2003, The Regents of the University of California, through +Lawrence Berkeley National Laboratory (subject to receipt of any required +approvals from U.S. Dept. of Energy) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +(1) Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +(2) Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +(3) Neither the name of Lawrence Berkeley National Laboratory, U.S. Dept. of +Energy 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. diff --git a/stuff/config/current.txt b/stuff/config/current.txt new file mode 100644 index 0000000..528f706 --- /dev/null +++ b/stuff/config/current.txt @@ -0,0 +1,1269 @@ + + + + "addFx" "Add" + "addFx.value" "Intensity" + "blendFx" "Cross Dissolve" + "blendFx.value" "Intensity" + "colorBurnFx" "Color Burn" + "colorDodgeFx" "Color Dodge" + "multFx" "Multiply" + "multFx.value" "Intensity" + "multFx.matte" "Alpha" + "screenFx" "Screen" + "subFx" "Subtract" + "subFx.matte" "Alpha" + "STD_toneCurveFx" "Curves" + "STD_toneCurveFx.curve" "" + "STD_adjustLevelsFx" "Adjust Levels" + "STD_adjustLevelsFx.in_rgb" "RGB Input" + "STD_adjustLevelsFx.out_rgb" "RGB Output" + "STD_adjustLevelsFx.in_r" "Red Input" + "STD_adjustLevelsFx.out_r" "Red Output" + "STD_adjustLevelsFx.in_g" "Green Input" + "STD_adjustLevelsFx.out_g" "Green Output" + "STD_adjustLevelsFx.in_b" "Blue Input" + "STD_adjustLevelsFx.out_b" "Blue Output" + "STD_adjustLevelsFx.in_m" "Alpha Input" + "STD_adjustLevelsFx.out_m" "Alpha Output" + "STD_adjustLevelsFx.gamma_rgb" "RGB Gamma" + "STD_adjustLevelsFx.gamma_r" "Red Gamma" + "STD_adjustLevelsFx.gamma_g" "Green Gamma" + "STD_adjustLevelsFx.gamma_b" "Blue Gamma" + "STD_adjustLevelsFx.gamma_m" "Alpha Gamma" + + "STD_backlitFx" "Backlit" + "STD_backlitFx.value" "Intensity" + "STD_backlitFx.color" "Color" + "STD_backlitFx.fade" "Fade" + + + "STD_blurFx" "Blur" + "STD_blurFx.value" "Value" + "STD_blurFx.spread" "Spread" + "STD_despeckleFx" "Despeckle" + "STD_despeckleFx.size" "Size" + "STD_despeckleFx.detect_speckles_on" "Detect On" + + "STD_directionalBlurFx" "Directional Blur" + "STD_directionalBlurFx.angle" "Angle" + "STD_directionalBlurFx.intensity" "Intensity" + "STD_directionalBlurFx.bidirectional" "Bidirectional" + "STD_directionalBlurFx.spread" "Spread" + + "STD_brightContFx" "Brightness Contrast" + "STD_brightContFx.brightness""Brightness" + "STD_brightContFx.contrast" "Contrast" + + "STD_bodyHighLightFx" "Body Highlight" + "STD_bodyHighLightFx.mode" "Mode" + "STD_bodyHighLightFx.point" "Offset X" + "STD_bodyHighLightFx.transparency" "Transparency" + "STD_bodyHighLightFx.blur" "Blur" + "STD_bodyHighLightFx.color" "Color" + "STD_bodyHighLightFx.invert" "Invert" + + + "STD_castShadowFx" "Cast Shadow" + "STD_castShadowFx.distort_type" "Mode" + "STD_castShadowFx.bottom_left_a" "Bottom Left X" + "STD_castShadowFx.bottom_left_b" "Bottom Left X" + "STD_castShadowFx.bottom_right_a" "Bottom Right X" + "STD_castShadowFx.bottom_right_b" "Bottom Right X" + "STD_castShadowFx.top_right_a" "Top Right X" + "STD_castShadowFx.top_right_b" "Top Right X" + "STD_castShadowFx.top_left_a" "Top Left X" + "STD_castShadowFx.top_left_b" "Top Left X" + "STD_castShadowFx.deactivate" " Deactivate" + "STD_castShadowFx.color" "Color" + "STD_castShadowFx.fade" "Fade" + "STD_castShadowFx.up_transp" "Top Transp" + "STD_castShadowFx.down_transp" "Bottom Transp" + "STD_castShadowFx.up_blur" "Top Blur" + "STD_castShadowFx.down_blur" "Bottom Blur" + + + "STD_changeColorFx" "Change Color" + "STD_changeColorFx.range" "Range" + "STD_changeColorFx.falloff" "Falloff" + "STD_changeColorFx.from_color" "FromColor" + "STD_changeColorFx.to_color" "ToColor" + + "STD_channelMixerFx" "Channel Mixer" + "STD_channelMixerFx.red_to_red" "Red to Red" + "STD_channelMixerFx.green_to_red" "Green to Red" + "STD_channelMixerFx.blue_to_red" "Blue to Red" + "STD_channelMixerFx.matte_to_red" "Alpha to Red" + "STD_channelMixerFx.red_to_green" "Red to Green" + "STD_channelMixerFx.green_to_green" "Green to Green" + "STD_channelMixerFx.blue_to_green" "Blue to Green" + "STD_channelMixerFx.matte_to_green" "Alpha to Green" + "STD_channelMixerFx.red_to_blue" "Red to Blue" + "STD_channelMixerFx.green_to_blue" "Green to Blue" + "STD_channelMixerFx.blue_to_blue" "Blue to Blue" + "STD_channelMixerFx.matte_to_blue" "Alpha to Blue" + "STD_channelMixerFx.red_to_matte" "Red to Alpha" + "STD_channelMixerFx.green_to_matte" "Green to Alpha" + "STD_channelMixerFx.blue_to_matte" "Blue to Alpha" + "STD_channelMixerFx.matte_to_matte" "Alpha to Alpha" + + "STD_cloudsFx" "Clouds" + "STD_cloudsFx.type" "Type" + "STD_cloudsFx.size" "Size" + "STD_cloudsFx.min" "Min" + "STD_cloudsFx.max" "Max" + "STD_cloudsFx.evolution" "Evolution" + "STD_cloudsFx.colors" "Colors" + + "STD_colorEmbossFx" "Color Emboss" + "STD_colorEmbossFx.intensity" "Intensity" + "STD_colorEmbossFx.elevation" "Distance" + "STD_colorEmbossFx.direction" "Direction" + "STD_colorEmbossFx.radius" "Radius" + + "STD_cornerPinFx" "Pinned Texture" + "STD_cornerPinFx.distort_type" "Mode" + "STD_cornerPinFx.mode" "Mode" + "STD_cornerPinFx.value" "Value" + "STD_cornerPinFx.keep" "Action" + "STD_cornerPinFx.bottom_left_a" "Bottom Left X" + "STD_cornerPinFx.bottom_left_b" "Bottom Left X" + "STD_cornerPinFx.bottom_right_a" "Bottom Right X" + "STD_cornerPinFx.bottom_right_b" "Bottom Right X" + "STD_cornerPinFx.top_right_a" "Top Right X" + "STD_cornerPinFx.top_right_b" "Top Right X" + "STD_cornerPinFx.top_left_a" "Top Left X" + "STD_cornerPinFx.top_left_b" "Top Left X" + "STD_cornerPinFx.deactivate" " Deactivate" + "STD_cornerPinFx.pattern" "Pattern" + + "STD_diamondGradientFx" "Diamond Gradient" + "STD_diamondGradientFx.colors" "Colors" + "STD_diamondGradientFx.size" "Size" + + "STD_dissolveFx" "Dissolve" + "STD_dissolveFx.intensity" "Intensity" + "STD_embossFx" "Emboss" + "STD_embossFx.intensity" "Intensity" + "STD_embossFx.elevation" "Distance" + "STD_embossFx.direction" "Direction" + "STD_embossFx.radius" "Radius" + + "STD_sharpenFx" "Sharpen" + "STD_sharpenFx.intensity" "Intensity" + "STD_externalPaletteFx" "External Palette" + + "STD_fadeFx" "Transparency" + "STD_fadeFx.value" "Intensity" + + "STD_fourPointsGradientFx" "Four Points" + "STD_fourPointsGradientFx.Point_1" "Point 1 X" + "STD_fourPointsGradientFx.Color_1" "Color 1" + "STD_fourPointsGradientFx.Point_2" "Point 2 X" + "STD_fourPointsGradientFx.Color_2" "Color 2" + "STD_fourPointsGradientFx.Point_3" "Point 3 X" + "STD_fourPointsGradientFx.Color_3" "Color 3" + "STD_fourPointsGradientFx.Point_4" "Point 4 X" + "STD_fourPointsGradientFx.Color_4" "Color 4" + + "STD_freeDistortFx" "Free Distort" + "STD_freeDistortFx.distort_type" "Mode" + "STD_freeDistortFx.bottom_left_a" "Bottom Left X" + "STD_freeDistortFx.bottom_left_b" "Bottom Left X" + "STD_freeDistortFx.bottom_right_a" "Bottom Right X" + "STD_freeDistortFx.bottom_right_b" "Bottom Right X" + "STD_freeDistortFx.top_right_a" "Top Right X" + "STD_freeDistortFx.top_right_b" "Top Right X" + "STD_freeDistortFx.top_left_a" "Top Left X" + "STD_freeDistortFx.top_left_b" "Top Left X" + "STD_freeDistortFx.deactivate" " Deactivate" + + "STD_gammaFx" "Gamma" + "STD_gammaFx.value" "Value" + + "STD_colorRaylitFx" "Color Raylit" + "STD_colorRaylitFx.p" "Center X" + "STD_colorRaylitFx.z" "Distance" + "STD_colorRaylitFx.intensity" "Intensity" + "STD_colorRaylitFx.decay" "Decay" + "STD_colorRaylitFx.smoothness" "Smoothness" + "STD_colorRaylitFx.includeInput" "Keep Image" + + "STD_erodeDilateFx" "Erode/Dilate" + "STD_erodeDilateFx.radius" "Radius" + "STD_erodeDilateFx.type" "Type" + + "STD_glowFx" "Glow" + "STD_glowFx.value" "Blur" + "STD_glowFx.brightness" "Brightness" + "STD_glowFx.color" "Color" + "STD_glowFx.fade" "Fade" + + "STD_hsvAdjustFx" "HSVAdjust" + "STD_hsvAdjustFx.hue" "Hue" + "STD_hsvAdjustFx.saturation" "Saturation" + "STD_hsvAdjustFx.value" "Value" + + "STD_hsvKeyFx" "HSV Key" + "STD_hsvKeyFx.h" "Hue" + "STD_hsvKeyFx.s" "Saturation" + "STD_hsvKeyFx.v" "Value" + "STD_hsvKeyFx.h_range" "H Range" + "STD_hsvKeyFx.s_range" "S Range" + "STD_hsvKeyFx.v_range" "V Range" + "STD_hsvKeyFx.invert" "Invert" + + + "STD_hsvScaleFx" "HSV Scale" + "STD_hsvScaleFx.hue" "Hue" + "STD_hsvScaleFx.saturation" "Saturation" + "STD_hsvScaleFx.value" "Value" + "STD_hsvScaleFx.hue_scale" "Hue Scale" + "STD_hsvScaleFx.saturation_scale" "Saturation Scale" + "STD_hsvScaleFx.value_scale" "Value Scale" + + "STD_kaleidoFx" "Kaleido" + "STD_kaleidoFx.center" "Center X" + "STD_kaleidoFx.angle" "Angle" + "STD_kaleidoFx.count" "Count" + + "STD_lightSpotFx" "Light Spot" + "STD_lightSpotFx.softness" "Softness" + "STD_lightSpotFx.a" "Width" + "STD_lightSpotFx.b" "Height" + "STD_lightSpotFx.color" "Color" + + "STD_linearGradientFx" "Linear Gradient" + "STD_linearGradientFx.period" "Size" + "STD_linearGradientFx.wave_amplitude" "Amplitude" + "STD_linearGradientFx.wave_frequency" "Frequency" + "STD_linearGradientFx.wave_phase" "Phase" + "STD_linearGradientFx.color1" "Color 1" + "STD_linearGradientFx.color2" "Color 2" + + "STD_linearWaveFx" "Linear Wave" + "STD_linearWaveFx.period" "Period" + "STD_linearWaveFx.count" "Quantity" + "STD_linearWaveFx.cycle" "Phase" + "STD_linearWaveFx.amplitude" "Amplitude" + "STD_linearWaveFx.frequency" "Frequency" + "STD_linearWaveFx.phase" "Cycle" + "STD_linearWaveFx.angle" "Angle" + "STD_linearWaveFx.intensity" "Intensity" + "STD_linearWaveFx.sensitivity" "Sensitivity" + "STD_linearWaveFx.sharpen" "Sharpen" + + "STD_mosaicFx" "Mosaic" + "STD_mosaicFx.size" "Size" + "STD_mosaicFx.distance" "Distance" + "STD_mosaicFx.bg_color" "BG Color" + "STD_mosaicFx.shape" "Shape" + + "STD_multiLinearGradientFx" "Multi Linear Gradient" + "STD_multiLinearGradientFx.period" "Period" + "STD_multiLinearGradientFx.count" "Quantity" + "STD_multiLinearGradientFx.cycle" "Cycle" + "STD_multiLinearGradientFx.wave_amplitude" "Amplitude" + "STD_multiLinearGradientFx.wave_frequency" "Frequency" + "STD_multiLinearGradientFx.wave_phase" "Phase" + "STD_multiLinearGradientFx.colors" "Colors" + + "STD_localBlurFx" "Local Blur" + "STD_localBlurFx.value" "Intensity" + + "STD_localTransparencyFx" "Local Transparency" + "STD_localTransparencyFx.value" "Intensity" + + "STD_motionBlurFx" "Motion Blur" + "STD_motionBlurFx.intensity" "Intensity" + "STD_motionBlurFx.spread" "Spread" + "STD_multiToneFx" "Multitone" + "STD_multiToneFx.colors" "Colors" + + "STD_noiseFx" "Noise" + "STD_noiseFx.Intensity" "Intensity" + "STD_noiseFx.Red" "Red" + "STD_noiseFx.Green" "Green" + "STD_noiseFx.Blue" "Blue" + "STD_noiseFx.Black_White" "Black & White" + "STD_noiseFx.Animate" "Random Animation" + + "STD_paletteFilterFx" "Palette Filter" + "STD_paletteFilterFx.keep" "Action" + "STD_paletteFilterFx.type" "Apply To" + + + + + + "STD_perlinNoiseFx" "Perlin Noise" + "STD_perlinNoiseFx.size" "Size" + "STD_perlinNoiseFx.type" "Type" + "STD_perlinNoiseFx.evolution" "Evolution" + "STD_perlinNoiseFx.intensity" "Intensity" + "STD_perlinNoiseFx.offsetx" "Horiz Offset" + "STD_perlinNoiseFx.offsety" "Vert Offset" + "STD_perlinNoiseFx.matte" "Alpha" + + "STD_posterizeFx" "Posterize" + "STD_posterizeFx.levels" "Levels" + + "STD_premultiplyFx" "Premultiply" + + + "STD_radialBlurFx" "Radial Blur" + "STD_radialBlurFx.point" "Center X" + "STD_radialBlurFx.radius" "Radius" + "STD_radialBlurFx.blur" "Blur" + + + "STD_radialGradientFx" "Radial Gradient" + "STD_radialGradientFx.period" "Outer Size" + "STD_radialGradientFx.innerperiod" "Inner Size" + "STD_radialGradientFx.color1" "Color 1" + "STD_radialGradientFx.color2" "Color 2" + + + "STD_randomWaveFx" "Random Wave" + "STD_randomWaveFx.evolution" "Evolution" + "STD_randomWaveFx.positionx" "Position X" + "STD_randomWaveFx.positiony" "Position Y" + "STD_randomWaveFx.intensity" "Intensity" + "STD_randomWaveFx.sensitivity" "Sensitivity" + "STD_randomWaveFx.sharpen" "Sharpen" + + + "STD_rippleFx" "Ripple" + "STD_rippleFx.period" "Period" + "STD_rippleFx.count" "Quantity" + "STD_rippleFx.cycle" "Cycle" + "STD_rippleFx.center" "Center X" + "STD_rippleFx.scalex" "Scale X" + "STD_rippleFx.scaley" "Scale Y" + "STD_rippleFx.angle" "Angle" + "STD_rippleFx.intensity" "Intensity" + "STD_rippleFx.sensitivity" "Sensitivity" + "STD_rippleFx.sharpen" "Sharpen" + + "STD_rotationalBlurFx" "Spin Blur" + "STD_rotationalBlurFx.point" "Center X" + "STD_rotationalBlurFx.radius" "Radius" + "STD_rotationalBlurFx.blur" "Blur" + + + "STD_multiRadialGradientFx" "Multi Radial Gradient" + "STD_multiRadialGradientFx.period" "Period" + "STD_multiRadialGradientFx.count" "Quantity" + "STD_multiRadialGradientFx.cycle" "Cycle" + "STD_multiRadialGradientFx.colors" "Colors" + + "STD_raylitFx" "Raylit" + "STD_raylitFx.p" "Center X" + "STD_raylitFx.z" "Distance" + "STD_raylitFx.color" "Color" + "STD_raylitFx.intensity" "Intensity" + "STD_raylitFx.decay" "Decay" + "STD_raylitFx.smoothness" "Smoothness" + "STD_raylitFx.includeInput" "Keep Image" + "STD_raylitFx.invert" "Invert" + + "STD_rgbKeyFx" "RGB Key" + "STD_rgbKeyFx.color" "Color" + "STD_rgbKeyFx.r_range" "Red Range" + "STD_rgbKeyFx.g_range" "Green Range" + "STD_rgbKeyFx.b_range" "Blue Range" + "STD_rgbKeyFx.invert" "Invert" + + "STD_rgbmCutFx" "RGBA Cut" + "STD_rgbmCutFx.r_range" "Red" + "STD_rgbmCutFx.g_range" "Green" + "STD_rgbmCutFx.b_range" "Blue" + "STD_rgbmCutFx.m_range" "Alpha" + + "STD_rgbmScaleFx" "RGBA Scale" + "STD_rgbmScaleFx.red" "Red" + "STD_rgbmScaleFx.green" "Green" + "STD_rgbmScaleFx.blue" "Blue" + "STD_rgbmScaleFx.matte" "Alpha" + + "STD_rgbmFadeFx" "RGB Fade" + "STD_rgbmFadeFx.color" "Color" + "STD_rgbmFadeFx.intensity" "Intensity" + + "STD_saltpepperNoiseFx" "Salt Pepper Noise" + "STD_saltpepperNoiseFx.Intensity" "Intensity" + "STD_saltpepperNoiseFx.Animate" "Random Animation" + + "STD_solarizeFx" "Solarize" + "STD_solarizeFx.maximum" "Intensity" + "STD_solarizeFx.peak_edge" "Peak Edge" + + "STD_spiralFx" "Spiral" + "STD_spiralFx.freq" "Frequency" + "STD_spiralFx.phase" "Phase" + "STD_spiralFx.colors" "Colors" + + "STD_squareGradientFx" "Square Gradient" + "STD_squareGradientFx.colors" "Colors" + "STD_squareGradientFx.size" "Size" + + "STD_targetSpotFx" "Target Spot" + "STD_targetSpotFx.z" "Distance" + "STD_targetSpotFx.angle" "Angle" + "STD_targetSpotFx.decay" "Decay" + "STD_targetSpotFx.sizeX" "Width" + "STD_targetSpotFx.sizeY" "Height" + "STD_targetSpotFx.color" "Color" + + "STD_textureFx" "Texture" + "STD_textureFx.indexes" "Indexes" + "STD_textureFx.keep" "Action" + "STD_textureFx.mode" "Mode" + "STD_textureFx.value" "Value" + + "STD_tileFx" "Tile" + "STD_tileFx.mode" "Mode" + "STD_tileFx.xMirror" "Mirror Horizontally" + "STD_tileFx.yMirror""Mirror Vertically" + "STD_tileFx.margin" "Margin" + + "STD_unmultiplyFx" "Unmultiply" + + "STD_warpFx" "Warp" + "STD_warpFx.intensity" "Intensity" + "STD_warpFx.sensitivity" "Size" + "STD_warpFx.sharpen" "Sharpen" + + + + "STD_blendTzFx" "Color Blending" + "STD_blendTzFx.Color_Index" "Color indexes" + "STD_blendTzFx.Smoothness" "Smoothness" + "STD_blendTzFx.Amount" "Intensity" + "STD_blendTzFx.noBlending" "No Blending over Other Colors" + + "STD_calligraphicFx" "Calligraphic Line" + "STD_calligraphicFx.Color_Index" "Color Indexes" + "STD_calligraphicFx.Thickness" "Thickness" + "STD_calligraphicFx.Accuracy" "Smoothness" + "STD_calligraphicFx.Noise" "Noise" + "STD_calligraphicFx.Horizontal" "Horizontal" + "STD_calligraphicFx.upWDiagonal" "Up Diagonal" + "STD_calligraphicFx.Vertical" "Vertical" + "STD_calligraphicFx.doWDiagonal" "Down Diagonal" + + "STD_outBorderFx" "Outline" + "STD_outBorderFx.Thickness" "Thickness" + "STD_outBorderFx.Accuracy" "Smoothness" + "STD_outBorderFx.Noise" "Noise" + "STD_outBorderFx.Horizontal" "Horizontal" + "STD_outBorderFx.upWDiagonal" "Up Diagonal" + "STD_outBorderFx.Vertical" "Vertical" + "STD_outBorderFx.doWDiagonal" "Down Diagonal" + + "STD_artContourFx" "Art Contour" + "STD_artContourFx.Color_Index" "Color Indexes" + "STD_artContourFx.Keep_color" "Keep Color" + "STD_artContourFx.Keep_Line" "Keep Line" + "STD_artContourFx.Include_Alpha" "Include Alpha" + "STD_artContourFx.Density" "Density" + "STD_artContourFx.Distance" "Distance" + "STD_artContourFx.Randomness" "Absolute Orientation" + "STD_artContourFx.Orientation" "Orientation" + "STD_artContourFx.Size" "Size" + + "STD_cornerPinFx.indexes" "Color Indexes" + "STD_paletteFilterFx.indexes" "Color Indexes" + + + + "SHADER_caustics" "Caustics" + "SHADER_caustics.color" "Water Color" + "SHADER_caustics.time" "Evolution" + + "SHADER_fireball" "Fireball" + "SHADER_fireball.color1" "Color 1" + "SHADER_fireball.color2" "Color 2" + "SHADER_fireball.detail" "Detail" + "SHADER_fireball.time" "Evolution" + + "SHADER_glitter" "Glitter" + "SHADER_glitter.threshold" "Threshold" + "SHADER_glitter.brightness" "Brightness" + "SHADER_glitter.radius" "Radius" + "SHADER_glitter.angle" "Angle" + "SHADER_glitter.halo" "Halo" + + "SHADER_starsky" "Star Sky" + "SHADER_starsky.color" "Cloud Color" + "SHADER_starsky.time" "Evolution" + "SHADER_starsky.brightness" "Brightness" + + "SHADER_sunflare" "Sun Flare" + "SHADER_sunflare.color" "Color" + "SHADER_sunflare.angle" "Angle" + "SHADER_sunflare.blades" "Rays" + "SHADER_sunflare.intensity" "Intensity" + "SHADER_sunflare.bias" "Bias" + "SHADER_sunflare.sharpness" "Sharpness" + + "SHADER_wavy" "Wavy" + "SHADER_wavy.color1" "Color 1" + "SHADER_wavy.color2" "Color 2" + "SHADER_wavy.time" "Evolution" + + "SHADER_radialblurGPU" "GPU Radial Blur" + "SHADER_radialblurGPU.center" "Center" + "SHADER_radialblurGPU.radius" "Safe Radius" + "SHADER_radialblurGPU.blur" "Blur Factor" + + "SHADER_spinblurGPU" "GPU Spin Blur" + "SHADER_spinblurGPU.center" "Center" + "SHADER_spinblurGPU.radius" "Safe Radius" + "SHADER_spinblurGPU.blur" "Blur" + + + + + "T3K_t3kCompositorFx" "T3kCompositor" + + "ZBASE_drawmaskFx" "DrawMask" + "ZBASE_drawmaskFx.OutlineOnly" "OutlineOnly" + + "ZBASE_masklayer" "MaskLayer" + "ZBASE_masklayer.feather" "Feather" + "ZBASE_masklayer.invert" "Invert" + "ZBASE_masklayer.mode" "Mode" + "ZBASE_masklayer.antialias" "Antialias" + + "ZBASE_paintFx" "Paint" + + "ZCOMP_TrackerFx" "Tracker" + "ZCOMP_TrackerFx.inputPoint" "InPoint" + "ZCOMP_TrackerFx.outputPoint" "OutPoint" + + "ZCOMP_affineGlFx" "AffineGl" + "ZCOMP_affineGlFx.xPos" "xPos" + "ZCOMP_affineGlFx.yPos" "yPos" + "ZCOMP_affineGlFx.zRot" "zRot" + "ZCOMP_affineGlFx.xCenter" "xCenter" + "ZCOMP_affineGlFx.yCenter" "yCenter" + "ZCOMP_affineGlFx.xScale" "xScale" + "ZCOMP_affineGlFx.yScale" "yScale" + + "ZCOMP_blueGreenKeyer" "ChromaKeyer" + "ZCOMP_blueGreenKeyer.outputMode" "OutputMode" + "ZCOMP_blueGreenKeyer.keyColor" "Color" + "ZCOMP_blueGreenKeyer.fgFilter" "FgFilter" + "ZCOMP_blueGreenKeyer.bgFilter" "BgFilter" + "ZCOMP_blueGreenKeyer.a2" "a2" + "ZCOMP_blueGreenKeyer.dilate" "Dilate" + "ZCOMP_blueGreenKeyer.fgThreshold" "FgThreshold" + "ZCOMP_blueGreenKeyer.bgThreshold" "BgThreshold" + "ZCOMP_blueGreenKeyer.blurRadius" "Blur" + "ZCOMP_blueGreenKeyer.erodeRadius" "Erode" + "ZCOMP_blueGreenKeyer.accuracy" "Accuracy" + "ZCOMP_blueGreenKeyer.spillSuppression" "SpillSuppression" + "ZCOMP_blueGreenKeyer.showSegmentation" "ShowSegmentation" + "ZCOMP_blueGreenKeyer.fgCorrection" "FgCorrection" + "ZCOMP_blueGreenKeyer.bgCorrection" "BgCorrection" + "ZCOMP_blueGreenKeyer.correctionThreshold" "CorrectionThreshold" + "ZCOMP_blueGreenKeyer.fgRadius" "FgRadius" + "ZCOMP_blueGreenKeyer.bgRadius" "BgRadius" + + "ZCOMP_lumaKeyer" "LumaKeyer" + "ZCOMP_lumaKeyer.OpacityLower" "Opacity Min" + "ZCOMP_lumaKeyer.OpacityUpper" "Opacity Max" + "ZCOMP_lumaKeyer.TransparencyLower" "Transparency Min" + "ZCOMP_lumaKeyer.TransparencyUpper" "Transparency Max" + "ZCOMP_lumaKeyer.blurRadius" "Blur" + "ZCOMP_lumaKeyer.erodeRadius" "Erode" + + "ZCOMP_spillSuppress" "SpillSuppress" + "ZCOMP_spillSuppress.color" "Color" + + "ZCOMP_zMultiLayerFx" "MultiLayer" + "ZCOMP_zMultiLayerFx.param0" "Mode" + + "ZCOMP_zStabilizerFx" "Stabilyzer" + "ZCOMP_zStabilizerFx.a" "a" + "ZCOMP_zStabilizerFx.a_w" "a_w" + "ZCOMP_zStabilizerFx.a_h" "a_h" + "ZCOMP_zStabilizerFx.a_status" "a_status" + "ZCOMP_zStabilizerFx.b" "b" + "ZCOMP_zStabilizerFx.b_w" "b_w" + "ZCOMP_zStabilizerFx.b_h" "b_h" + "ZCOMP_zStabilizerFx.b_status" "b_status" + "ZCOMP_zStabilizerFx.c" "c" + "ZCOMP_zStabilizerFx.c_w" "c_w" + "ZCOMP_zStabilizerFx.c_h" "" + "ZCOMP_zStabilizerFx.c_status" "c_status" + "ZCOMP_zStabilizerFx.interp_a" "interp_a" + "ZCOMP_zStabilizerFx.interp_b" "interp_b" + "ZCOMP_zStabilizerFx.interp_c" "interp_c" + + "ZCOMP_degrainFx" "Degrain" + "ZCOMP_degrainFx.red" "Red" + "ZCOMP_degrainFx.green" "Green" + "ZCOMP_degrainFx.blue" "Blue" + "ZCOMP_degrainFx.alpha" "Alpha" + "ZCOMP_degrainFx.radius" "Radius" + + "ZCOMP_basicKeyerFx" "BasicKeyer" + "ZCOMP_basicKeyerFx.keyColor" "Color" + "ZCOMP_basicKeyerFx.outputMode" "OutputMode" + "ZCOMP_basicKeyerFx.lowerBoundThreshold" "LowerBound" + "ZCOMP_basicKeyerFx.a2" "MatteStrength" + "ZCOMP_basicKeyerFx.upperBoundThreshold" "UpperBound" + "ZCOMP_basicKeyerFx.blurRadius" "Blur" + "ZCOMP_basicKeyerFx.erodeRadius" "Erode" + "ZCOMP_basicKeyerFx.spillSuppress" "SpillSuppression" + + "ZCOMP_spillRevealFx" "SpillReveal" + "ZCOMP_spillRevealFx.color" "Color" + "ZCOMP_spillRevealFx.luma" "Luma" + "ZCOMP_spillRevealFx.smoothRadius" "Smoothness" + + + + "affineFx" "Move2D" + "affineFx.xPos" "xPos" + "affineFx.yPos" "yPos" + "affineFx.zRot" "zRot" + "affineFx.xCenter" "xCenter" + "affineFx.yCenter" "yCenter" + "affineFx.xScale" "xScale" + "affineFx.yScale" "yScale" + "affineFx.useMMX" "useMMX" + "affineFx.useMotionPath" "MotionPath" + "affineFx.usePathAim" "PathAim" + + "atopFx" "Visible Matte In" + + "checkBoardFx" "Checkerboard" + "checkBoardFx.color1" "Color 1" + "checkBoardFx.color2" "Color 2" + "checkBoardFx.size" "Size" + + "colorCardFx" "Color Card" + "colorCardFx.color" "Color" + + "cropFx" "Crop" + "cropFx.left" "Left" + "cropFx.bottom" "Bottom" + "cropFx.right" "Right" + "cropFx.top" "Top" + + "crossFx" "Blend" + "crossFx.value" "Intensity" + + "deInterlaceFx" "Deinterlace" + "deInterlaceFx.convert" "Convert" + "deInterlaceFx.method" "Method" + "deInterlaceFx.field_separation" "Field Separation" + + "fieldFx" "Field" + "fieldFx.fieldprevalence" "FieldPrevalence" + + "inFx" "Matte In" + + "interlaceFx" "Interlace" + + "invertFx" "Invert" + "invertFx.red_channel" "Red" + "invertFx.green_channel" "Green" + "invertFx.blue_channel" "Blue" + "invertFx.alpha_channel" "Alpha" + + "lightenFx" "Lighten" + + "linearBurnFx" "Linear Burn" + + "localTransparencyFx" "Local Transparency" + "localTransparencyFx.value" "Intensity" + + "markCenterFx" "Mark Center" + "markCenterFx.color" "Color" + "markCenterFx.size" "Size" + + "maxFx" "Lighten" + + "minFx" "Darken" + "minFx.matte" "Alpha" + + "moveFx" "Move" + "moveFx.dx" "PanX" + "moveFx.dy" "PanY" + + "outFx" "Matte Out" + + "overFx" "Over" + + "overlayFx" "Overlay" + + "readImageFx" "StillImageIn" + "readImageFx.filename" "FilePath" + "readImageFx.doPremultiply" "Premultiply" + "readImageFx.doInvertAlpha" "InvertAlpha" + + "readLevelFx" "ClipIn" + "readLevelFx.filename" "FilePath" + "readLevelFx.field_separation" "FieldSeparation" + "readLevelFx.doPremultiply" "Premultiply" + "readLevelFx.doInvertAlpha" "InvertAlpha" + + "scaleFx" "Scale" + "scaleFx.xScale" "xScale" + "scaleFx.yScale" "yScale" + + + + "swapFieldsFx" "SwapFields" + + "xorFx" "XOr" + + + + + Selective "Selective" "Selective" "Selective" + ToolSize "Size" "Size" "Size" + + + + + STD_particlesFx "Particles" + "STD_particlesFx.source_ctrl" "Control Image" + "STD_particlesFx.bright_thres" "Threshold" + "STD_particlesFx.multi_source" "Multiple Generators in Control Image" + "STD_particlesFx.center" "Center X" + "STD_particlesFx.length" "Width" + "STD_particlesFx.height" "Height" + "STD_particlesFx.animation" "Animation" + "STD_particlesFx.step" "Animation Step" + "STD_particlesFx.starting_frame" "Starting Frame" + "STD_particlesFx.birth_rate" "Birth Rate" + "STD_particlesFx.random_seed" "Random Seed" + "STD_particlesFx.lifetime" "Lifetime" + "STD_particlesFx.lifetime_ctrl" "Control Image" + "STD_particlesFx.column_lifetime" "Use Column Duration for Lifetime" + "STD_particlesFx.gravity" "Gravity" + "STD_particlesFx.gravity_angle" "Gravity Angle" + "STD_particlesFx.gravity_ctrl" "Control Image" + "STD_particlesFx.friction" "Friction" + "STD_particlesFx.friction_ctrl" "Control Image" + "STD_particlesFx.wind" "Wind Intensity" + "STD_particlesFx.wind_angle" "Wing Angle" + "STD_particlesFx.swing_mode" "Swing Mode" + "STD_particlesFx.scattering_x" "Horizontal" + "STD_particlesFx.scattering_y" "Vertical" + "STD_particlesFx.scattering_x_ctrl" "H Control Image" + "STD_particlesFx.scattering_y_ctrl" "V Control Image" + "STD_particlesFx.swing" "Swing" + "STD_particlesFx.speed" "Speed" + "STD_particlesFx.speed_ctrl" "Control Image" + "STD_particlesFx.speed_angle" "Speed Angle" + + "STD_particlesFx.speeda_ctrl" "Control Image" + "STD_particlesFx.speeda_use_gradient" "Use Gradient Angle" + + "STD_particlesFx.speed_size" "Linked to Scale" + "STD_particlesFx.top_layer" "Top Layer" + "STD_particlesFx.mass" "Mass" + "STD_particlesFx.scale" "Size" + "STD_particlesFx.scale_ctrl" "Control Image" + "STD_particlesFx.scale_ctrl_all" "Use Control Image for the Whole Lifetime" + "STD_particlesFx.rot" "Orientation" + "STD_particlesFx.rot_ctrl" "Control Image" + "STD_particlesFx.trail" "Trail" + "STD_particlesFx.trail_step" "Step" + "STD_particlesFx.spin_swing_mode" "Swing Mode" + "STD_particlesFx.spin_speed" "Rotation Speed" + "STD_particlesFx.spin_random" "Extra Speed" + "STD_particlesFx.spin_swing" "Rotation Swing" + "STD_particlesFx.path_aim" "Follow Particles Movement" + "STD_particlesFx.opacity" "Opacity" + "STD_particlesFx.opacity_ctrl" "Control Image" + "STD_particlesFx.trail_opacity" "Trail Opacity" + "STD_particlesFx.scale_step" "Size Intensity" + "STD_particlesFx.scale_step_ctrl" "Control Image" + "STD_particlesFx.fade_in" "Fade-in Frames" + "STD_particlesFx.fade_out" "Fade-out Frames" + "STD_particlesFx.birth_color" "Birth Color" + "STD_particlesFx.birth_color_ctrl" "Control Image" + "STD_particlesFx.birth_color_spread" "Birth Spread" + "STD_particlesFx.birth_color_fade" "Birth Intensity" + "STD_particlesFx.fadein_color" "Fade-in Color" + "STD_particlesFx.fadein_color_ctrl" "Control Image" + "STD_particlesFx.fadein_color_spread" "Fade-in Spread" + "STD_particlesFx.fadein_color_range" "Frame Range" + "STD_particlesFx.fadein_color_fade" "Fade-in Intensity" + "STD_particlesFx.fadeout_color" "Fade-out Color" + "STD_particlesFx.fadeout_color_ctrl" "Control Image" + "STD_particlesFx.fadeout_color_spread" "Fade-out Spread" + "STD_particlesFx.fadeout_color_range" "Frame Range" + "STD_particlesFx.fadeout_color_fade" "Fade-out Intensity" + "STD_particlesFx.source_gradation" "Use Control Image Gradation" + "STD_particlesFx.pick_color_for_every_frame" "Pick Control Image's Color for Every Frame" + "STD_particlesFx.perspective_distribution" "Perspective Distribution" + + + + + W_NS "N/S" + W_EW "E/W" + W_Z "Z" + W_SO "SO" + W_Rotation "Rotation" + W_ScaleH "Scale H" + W_ScaleV "Scale V" + W_ShearH "Shear H" + W_ShearV "Shear V" + W_Scale "Scale" + + + + "STD_inoAddFx" "Add Ino" + "STD_inoAddFx.opacity" "Opacity" + "STD_inoAddFx.clipping_mask" "Clipping Mask" + "STD_inoColorBurnFx" "Color Burn Ino" + "STD_inoColorBurnFx.opacity" "Opacity" + "STD_inoColorBurnFx.clipping_mask" "Clipping Mask" + "STD_inoColorDodgeFx" "Color Dodge Ino" + "STD_inoColorDodgeFx.opacity" "Opacity" + "STD_inoColorDodgeFx.clipping_mask" "Clipping Mask" + "STD_inoCrossDissolveFx" "Cross Dissolve Ino" + "STD_inoCrossDissolveFx.opacity" "Opacity" + "STD_inoCrossDissolveFx.clipping_mask" "Clipping Mask" + "STD_inoDarkenFx" "Darken Ino" + "STD_inoDarkenFx.opacity" "Opacity" + "STD_inoDarkenFx.clipping_mask" "Clipping Mask" + "STD_inoDarkerColorFx" "Darker Color Ino" + "STD_inoDarkerColorFx.opacity" "Opacity" + "STD_inoDarkerColorFx.clipping_mask" "Clipping Mask" + "STD_inoDivideFx" "Divide Ino" + "STD_inoDivideFx.opacity" "Opacity" + "STD_inoDivideFx.clipping_mask" "Clipping Mask" + "STD_inoHardLightFx" "Hard Light Ino" + "STD_inoHardLightFx.opacity" "Opacity" + "STD_inoHardLightFx.clipping_mask" "Clipping Mask" + "STD_inoHardMixFx" "Hard Mix Ino" + "STD_inoHardMixFx.opacity" "Opacity" + "STD_inoHardMixFx.clipping_mask" "Clipping Mask" + "STD_inoLightenFx" "Lighten Ino" + "STD_inoLightenFx.opacity" "Opacity" + "STD_inoLightenFx.clipping_mask" "Clipping Mask" + "STD_inoLighterColorFx" "Lighter Color Ino" + "STD_inoLighterColorFx.opacity" "Opacity" + "STD_inoLighterColorFx.clipping_mask" "Clipping Mask" + "STD_inoLinearBurnFx" "Linear Burn Ino" + "STD_inoLinearBurnFx.opacity" "Opacity" + "STD_inoLinearBurnFx.clipping_mask" "Clipping Mask" + "STD_inoLinearDodgeFx" "Linear Dodge Ino" + "STD_inoLinearDodgeFx.opacity" "Opacity" + "STD_inoLinearDodgeFx.clipping_mask" "Clipping Mask" + "STD_inoLinearLightFx" "Linear Light Ino" + "STD_inoLinearLightFx.opacity" "Opacity" + "STD_inoLinearLightFx.clipping_mask" "Clipping Mask" + "STD_inoMultiplyFx" "Multiply Ino" + "STD_inoMultiplyFx.opacity" "Opacity" + "STD_inoMultiplyFx.clipping_mask" "Clipping Mask" + "STD_inoOverFx" "Over Ino" + "STD_inoOverFx.opacity" "Opacity" + "STD_inoOverFx.clipping_mask" "Clipping Mask" + "STD_inoOverlayFx" "Overlay Ino" + "STD_inoOverlayFx.opacity" "Opacity" + "STD_inoOverlayFx.clipping_mask" "Clipping Mask" + "STD_inoPinLightFx" "Pin Light Ino" + "STD_inoPinLightFx.opacity" "Opacity" + "STD_inoPinLightFx.clipping_mask" "Clipping Mask" + "STD_inoScreenFx" "Screen Ino" + "STD_inoScreenFx.opacity" "Opacity" + "STD_inoScreenFx.clipping_mask" "Clipping Mask" + "STD_inoSoftLightFx" "Soft Light Ino" + "STD_inoSoftLightFx.opacity" "Opacity" + "STD_inoSoftLightFx.clipping_mask" "Clipping Mask" + "STD_inoSubtractFx" "Subtract Ino" + "STD_inoSubtractFx.opacity" "Opacity" + "STD_inoSubtractFx.clipping_mask" "Clipping Mask" + "STD_inoSubtractFx.alpha_rendering" "Alpha Rendering" + "STD_inoVividLightFx" "Vivid Light Ino" + "STD_inoVividLightFx.opacity" "Opacity" + "STD_inoVividLightFx.clipping_mask" "Clipping Mask" + "STD_inoBlurFx" "Blur Ino" + "STD_inoBlurFx.radius" "Radius" + "STD_inoBlurFx.reference" "Reference" + "STD_inoChannelSelectorFx" "Channel Selector Ino" + "STD_inoChannelSelectorFx.red_source" "Red Source" + "STD_inoChannelSelectorFx.red_channel" "Red Channel" + "STD_inoChannelSelectorFx.green_source" "Green Source" + "STD_inoChannelSelectorFx.green_channel" "Green Channel" + "STD_inoChannelSelectorFx.blue_source" "Blue Source" + "STD_inoChannelSelectorFx.blue_channel" "Blue Channel" + "STD_inoChannelSelectorFx.alpha_source" "Alpha Source" + "STD_inoChannelSelectorFx.alpha_channel" "Alpha Channel" + "STD_inoDensityFx" "Density Ino" + "STD_inoDensityFx.density" "Density" + "STD_inoDensityFx.reference" "Reference" + "STD_inoFogFx" "Fog Ino" + "STD_inoFogFx.radius" "Radius" + "STD_inoFogFx.curve" "Curve" + "STD_inoFogFx.power" "Power" + "STD_inoFogFx.threshold_min" "Threshold Min" + "STD_inoFogFx.threshold_max" "Threshold Max" + "STD_inoFogFx.alpha_rendering" "Alpha Rendering" + "STD_inohlsAddFx" "HLS Add Ino" + "STD_inohlsAddFx.from_rgba" "From RGBA" + "STD_inohlsAddFx.offset" "Offset" + "STD_inohlsAddFx.hue" "Hue" + "STD_inohlsAddFx.lightness" "Lightness" + "STD_inohlsAddFx.saturation" "Saturation" + "STD_inohlsAddFx.alpha" "Alpha" + "STD_inohlsAddFx.anti_alias" "Premultiplied" + "STD_inohlsAddFx.reference" "Reference" + "STD_inohlsAdjustFx" "HLS Adjust Ino" + "STD_inohlsAdjustFx.pivot_hue" "Hue" + "STD_inohlsAdjustFx.pivot_lightness" "Lightness" + "STD_inohlsAdjustFx.pivot_saturation" "Saturation" + "STD_inohlsAdjustFx.scale_hue" "Hue" + "STD_inohlsAdjustFx.scale_lightness" "Lightness" + "STD_inohlsAdjustFx.scale_saturation" "Saturation" + "STD_inohlsAdjustFx.shift_hue" "Hue" + "STD_inohlsAdjustFx.shift_lightness" "Lightness" + "STD_inohlsAdjustFx.shift_saturation" "Saturation" + "STD_inohlsAdjustFx.anti_alias" "Premultiplied" + "STD_inohlsAdjustFx.reference" "Reference" + "STD_inohlsNoiseFx" "HLS Noise Ino" + "STD_inohlsNoiseFx.hue" "Hue" + "STD_inohlsNoiseFx.lightness" "Lightness" + "STD_inohlsNoiseFx.saturation" "Saturation" + "STD_inohlsNoiseFx.alpha" "Alpha" + "STD_inohlsNoiseFx.seed" "Seed" + "STD_inohlsNoiseFx.nblur" "NBlur" + "STD_inohlsNoiseFx.effective" "Effective" + "STD_inohlsNoiseFx.center" "Center" + "STD_inohlsNoiseFx.type" "Type" + "STD_inohlsNoiseFx.anti_alias" "Premultiplied" + "STD_inohlsNoiseFx.reference" "Reference" + "STD_inohsvAddFx" "HSV Add Ino" + "STD_inohsvAddFx.from_rgba" "From RGBA" + "STD_inohsvAddFx.offset" "Offset" + "STD_inohsvAddFx.hue" "Hue" + "STD_inohsvAddFx.saturation" "Saturation" + "STD_inohsvAddFx.value" "Value" + "STD_inohsvAddFx.alpha" "Alpha" + "STD_inohsvAddFx.anti_alias" "Premultiplied" + "STD_inohsvAddFx.reference" "Reference" + "STD_inohsvAdjustFx" "HSV Adjust Ino" + "STD_inohsvAdjustFx.pivot_hue" "Hue" + "STD_inohsvAdjustFx.pivot_saturation" "Saturation" + "STD_inohsvAdjustFx.pivot_value" "Value" + "STD_inohsvAdjustFx.scale_hue" "Hue" + "STD_inohsvAdjustFx.scale_saturation" "Saturation" + "STD_inohsvAdjustFx.scale_value" "Value" + "STD_inohsvAdjustFx.shift_hue" "Hue" + "STD_inohsvAdjustFx.shift_saturation" "Saturation" + "STD_inohsvAdjustFx.shift_value" "Value" + "STD_inohsvAdjustFx.anti_alias" "Premultiplied" + "STD_inohsvAdjustFx.reference" "Reference" + "STD_inohsvNoiseFx" "HSV Noise Ino" + "STD_inohsvNoiseFx.hue" "Hue" + "STD_inohsvNoiseFx.saturation" "Saturation" + "STD_inohsvNoiseFx.value" "Value" + "STD_inohsvNoiseFx.alpha" "Alpha" + "STD_inohsvNoiseFx.seed" "Seed" + "STD_inohsvNoiseFx.nblur" "NBlur" + "STD_inohsvNoiseFx.effective" "Effective" + "STD_inohsvNoiseFx.center" "Center" + "STD_inohsvNoiseFx.type" "Type" + "STD_inohsvNoiseFx.anti_alias" "Premultiplied" + "STD_inohsvNoiseFx.reference" "Reference" + "STD_inoLevelAutoFx" "Level Auto Ino" + "STD_inoLevelAutoFx.in_min_shift" "In Min Shift" + "STD_inoLevelAutoFx.in_max_shift" "In Max Shift" + "STD_inoLevelAutoFx.out_min" "Out Min" + "STD_inoLevelAutoFx.out_max" "Out Max" + "STD_inoLevelAutoFx.gamma" "Gamma" + "STD_inoLevelMasterFx" "Level Master Ino" + "STD_inoLevelMasterFx.in" "In" + "STD_inoLevelMasterFx.out" "Out" + "STD_inoLevelMasterFx.gamma" "Gamma" + "STD_inoLevelMasterFx.alpha_rendering" "Alpha Rendering" + "STD_inoLevelMasterFx.anti_alias" "Premultiplied" + "STD_inoLevelMasterFx.reference" "Reference" + "STD_inoLevelrgbaFx" "Level RGBA Ino" + "STD_inoLevelrgbaFx.red_in" "Red In" + "STD_inoLevelrgbaFx.red_out" "Red Out" + "STD_inoLevelrgbaFx.red_gamma" "Red Gamma" + "STD_inoLevelrgbaFx.gre_in" "Green In" + "STD_inoLevelrgbaFx.gre_out" "Green Out" + "STD_inoLevelrgbaFx.gre_gamma" "Green Gamma" + "STD_inoLevelrgbaFx.blu_in" "Blue In" + "STD_inoLevelrgbaFx.blu_out" "Blue Out" + "STD_inoLevelrgbaFx.blu_gamma" "Blue Gamma" + "STD_inoLevelrgbaFx.alp_in" "Alpha In" + "STD_inoLevelrgbaFx.alp_out" "Alpha Out" + "STD_inoLevelrgbaFx.alp_gamma" "Alpha Gamma" + "STD_inoLevelrgbaFx.anti_alias" "Premultiplied" + "STD_inoLevelrgbaFx.reference" "Reference" + "STD_inoLineBlurFx" "Line Blur Ino" + "STD_inoLineBlurFx.action_mode" "Action Mode" + "STD_inoLineBlurFx.blur_count" "Blur Count" + "STD_inoLineBlurFx.blur_power" "Blur Power" + "STD_inoLineBlurFx.blur_subpixel" "Blur Subpixel" + "STD_inoLineBlurFx.blur_near_ref" "Blur Near Ref" + "STD_inoLineBlurFx.blur_near_len" "Blur Near Len" + "STD_inoLineBlurFx.vector_smooth_retry" "Vector Smooth_retry" + "STD_inoLineBlurFx.vector_near_ref" "Vector Near Ref" + "STD_inoLineBlurFx.vector_near_len" "Vector Near Len" + "STD_inoLineBlurFx.smudge_thick" "Smudge Thick" + "STD_inoLineBlurFx.smudge_remain" "Smudge Remain" + "STD_inoMaxMinFx" "Max Min Ino" + "STD_inoMaxMinFx.max_min_select" "Max Min Select" + "STD_inoMaxMinFx.radius" "Radius" + "STD_inoMaxMinFx.polygon_number" "Polygon Number" + "STD_inoMaxMinFx.degree" "Degree" + "STD_inoMaxMinFx.alpha_rendering" "Alpha Rendering" + "STD_inoMaxMinFx.reference" "Reference" + "STD_inoMedianFx" "Median Ino" + "STD_inoMedianFx.radius" "Radius" + "STD_inoMedianFx.channel" "Channel" + "STD_inoMedianFx.reference" "Reference" + "STD_inoMedianFilterFx" "Median Filter Ino" + "STD_inoMedianFilterFx.radius" "Radius" + "STD_inoMedianFilterFx.channel" "Channel" + "STD_inoMedianFilterFx.reference" "Reference" + "STD_inoMotionBlurFx" "Motion Blur Ino" + "STD_inoMotionBlurFx.depend_move" "Depend Move" + "STD_inoMotionBlurFx.x1" "X1" + "STD_inoMotionBlurFx.y1" "Y1" + "STD_inoMotionBlurFx.x2" "X2" + "STD_inoMotionBlurFx.y2" "Y2" + "STD_inoMotionBlurFx.scale" "Scale" + "STD_inoMotionBlurFx.curve" "Curve" + "STD_inoMotionBlurFx.zanzo_length" "Zanzo Length" + "STD_inoMotionBlurFx.zanzo_power" "Zanzo Power" + "STD_inoMotionBlurFx.alpha_rendering" "Alpha Rendering" + "STD_inoMotionWindFx" "Motion Wind Ino" + "STD_inoMotionWindFx.direction" "Direction" + "STD_inoMotionWindFx.dark" "Dark" + "STD_inoMotionWindFx.alpha_rendering" "Alpha Rendering" + "STD_inoMotionWindFx.length_min" "Length Min" + "STD_inoMotionWindFx.length_max" "Length Max" + "STD_inoMotionWindFx.length_bias" "Length Bias" + "STD_inoMotionWindFx.length_seed" "Length Seed" + "STD_inoMotionWindFx.length_ref" "Length Ref" + "STD_inoMotionWindFx.force_min" "Force Min" + "STD_inoMotionWindFx.force_max" "Force Max" + "STD_inoMotionWindFx.force_bias" "Force Bias" + "STD_inoMotionWindFx.force_seed" "Force Seed" + "STD_inoMotionWindFx.force_ref" "Force Ref" + "STD_inoMotionWindFx.density_min" "Density Min" + "STD_inoMotionWindFx.density_max" "Density Max" + "STD_inoMotionWindFx.density_bias" "Density Bias" + "STD_inoMotionWindFx.density_seed" "Density Seed" + "STD_inoMotionWindFx.density_ref" "Density Ref" + "STD_inoMotionWindFx.reference" "Reference" + "STD_inoNegateFx" "Negate Ino" + "STD_inoNegateFx.red" "Red" + "STD_inoNegateFx.green" "Green" + "STD_inoNegateFx.blue" "Blue" + "STD_inoNegateFx.alpha" "Alpha" + "STD_inopnCloudsFx" "PN Clouds Ino" + "STD_inopnCloudsFx.size" "Size" + "STD_inopnCloudsFx.z" "Z" + "STD_inopnCloudsFx.octaves" "Octaves" + "STD_inopnCloudsFx.persistance" "Persistance" + "STD_inopnCloudsFx.alpha_rendering" "Alpha Rendering" + "STD_inoRadialBlurFx" "Radial Blur Ino" + "STD_inoRadialBlurFx.center" "Center" + "STD_inoRadialBlurFx.radius" "Radius" + "STD_inoRadialBlurFx.blur" "Blur" + "STD_inoRadialBlurFx.twist" "Twist" + "STD_inoRadialBlurFx.alpha_rendering" "Alpha Rendering" + "STD_inoRadialBlurFx.anti_alias" "Anti Alias" + "STD_inoRadialBlurFx.reference" "Reference" + "STD_inoSpinBlurFx" "Spin Blur Ino" + "STD_inoSpinBlurFx.center" "Center" + "STD_inoSpinBlurFx.radius" "Radius" + "STD_inoSpinBlurFx.blur" "Blur" + "STD_inoSpinBlurFx.type" "Type" + "STD_inoSpinBlurFx.alpha_rendering" "Alpha Rendering" + "STD_inoSpinBlurFx.anti_alias" "Anti Alias" + "STD_inoSpinBlurFx.reference" "Reference" + "STD_inoWarphvFx" "Warp HV Ino" + "STD_inoWarphvFx.h_maxlen" "H MaxLen" + "STD_inoWarphvFx.v_maxlen" "V MaxLen" + "STD_inoWarphvFx.h_ref_mode" "H reference" + "STD_inoWarphvFx.v_ref_mode" "V reference" + "STD_inoWarphvFx.alpha_rendering" "Alpha Rendering" + "STD_inoWarphvFx.anti_aliasing" "Anti Aliasing" + + + + "STD_iwa_AdjustExposureFx" "Adjust Exposure Iwa" + "STD_iwa_AdjustExposureFx.hardness" "Hardness" + "STD_iwa_AdjustExposureFx.scale" "Scale" + "STD_iwa_AdjustExposureFx.offset" "Offset" + + "STD_iwa_DirectionalBlurFx" "Directional Blur Iwa" + "STD_iwa_DirectionalBlurFx.angle" "Angle" + "STD_iwa_DirectionalBlurFx.intensity" "Intensity" + "STD_iwa_DirectionalBlurFx.bidirectional" "Bidirectional" + "STD_iwa_DirectionalBlurFx.filterType" "Filter Type" + + "STD_iwa_GradientWarpFx" "Gradient Warp Iwa" + "STD_iwa_GradientWarpFx.h_maxlen" "H Length" + "STD_iwa_GradientWarpFx.v_maxlen" "V Length" + "STD_iwa_GradientWarpFx.scale" "Scale" + + "STD_iwa_MotionBlurCompFx" "Motion Blur Iwa" + "STD_iwa_MotionBlurCompFx.hardness" "Hardness" + "STD_iwa_MotionBlurCompFx.shutterStart" "Shutter Start" + "STD_iwa_MotionBlurCompFx.shutterEnd" "Shutter End" + "STD_iwa_MotionBlurCompFx.traceResolution" "Trace Resolution" + "STD_iwa_MotionBlurCompFx.motionObjectType" "Reference Object" + "STD_iwa_MotionBlurCompFx.motionObjectIndex" "Index" + "STD_iwa_MotionBlurCompFx.startValue" "Start Value" + "STD_iwa_MotionBlurCompFx.startCurve" "Start Curve" + "STD_iwa_MotionBlurCompFx.endValue" "End Value" + "STD_iwa_MotionBlurCompFx.endCurve" "End Curve" + "STD_iwa_MotionBlurCompFx.zanzoMode" "Zanzo Mode" + "STD_iwa_MotionBlurCompFx.premultiType" "Source Premultiply Type" + + "STD_iwa_PerspectiveDistortFx" "Perspective Distort Iwa" + "STD_iwa_PerspectiveDistortFx.vanishingPoint" "Vanishing Point" + "STD_iwa_PerspectiveDistortFx.anchorPoint" "Anchor Point" + "STD_iwa_PerspectiveDistortFx.precision" "Precision" + + "STD_iwa_SpectrumFx" "Spectrum Iwa" + "STD_iwa_SpectrumFx.intensity" "Intensity" + "STD_iwa_SpectrumFx.refractiveIndex" "Refractive Index" + "STD_iwa_SpectrumFx.thickMax" "Thick Max" + "STD_iwa_SpectrumFx.thickMin" "Thick Min" + "STD_iwa_SpectrumFx.RGamma" "R Gamma" + "STD_iwa_SpectrumFx.GGamma" "G Gamma" + "STD_iwa_SpectrumFx.BGamma" "B Gamma" + "STD_iwa_SpectrumFx.lensFactor" "Lens Factor" + "STD_iwa_SpectrumFx.lightThres" "Light Threshod" + "STD_iwa_SpectrumFx.lightIntensity" "Light Intensity" + + "STD_iwa_TileFx" "Tile Iwa" + "STD_iwa_TileFx.inputSize" "Input Size" + "STD_iwa_TileFx.leftQuantity" "Left Quantity" + "STD_iwa_TileFx.rightQuantity" "Right Quantity" + "STD_iwa_TileFx.xMirror" "Mirror Horizontally" + "STD_iwa_TileFx.hMargin" "Horizontal Margin" + "STD_iwa_TileFx.topQuantity" "Top Quantity" + "STD_iwa_TileFx.bottomQuantity" "Bottom Quantity" + "STD_iwa_TileFx.yMirror" "Mirror Vertically" + "STD_iwa_TileFx.vMargin" "Vertical Margin" + + "STD_iwa_PNPerspectiveFx" "PN Perspective Iwa" + "STD_iwa_PNPerspectiveFx.renderMode" "Mode" + "STD_iwa_PNPerspectiveFx.noiseType" "Noise Type" + "STD_iwa_PNPerspectiveFx.size" "Size" + "STD_iwa_PNPerspectiveFx.evolution" "Evolution" + "STD_iwa_PNPerspectiveFx.octaves" "Octaves" + "STD_iwa_PNPerspectiveFx.offset" "Offset" + "STD_iwa_PNPerspectiveFx.persistance_intensity" "p_Intensity" + "STD_iwa_PNPerspectiveFx.persistance_size" "p_Size" + "STD_iwa_PNPerspectiveFx.persistance_evolution" "p_Evolution" + "STD_iwa_PNPerspectiveFx.persistance_offset" "p_Offset" + "STD_iwa_PNPerspectiveFx.fov" "Fov" + "STD_iwa_PNPerspectiveFx.eyeLevel" "Eye Level" + "STD_iwa_PNPerspectiveFx.alpha_rendering" "Alpha Rendering" + "STD_iwa_PNPerspectiveFx.waveHeight" "Wave Height" + + + + STD_iwa_TiledParticlesFx "Tiled Particles Iwa" + "STD_iwa_TiledParticlesFx.rendermode" "Render Mode" + + "STD_iwa_TiledParticlesFx.base_ctrl" "Base Image" + "STD_iwa_TiledParticlesFx.margin" "Margin" + "STD_iwa_TiledParticlesFx.curl" "Curl" + "STD_iwa_TiledParticlesFx.curl_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.curl_ctrl_2" "Control Image 2" + "STD_iwa_TiledParticlesFx.triangleSize" "Origin Spacing" + + "STD_iwa_TiledParticlesFx.flap_velocity" "Flap Velocity" + "STD_iwa_TiledParticlesFx.flap_dir_sensitivity" "Rotation Sensitivity" + "STD_iwa_TiledParticlesFx.flap_ctrl" "Control Image" + + "STD_iwa_TiledParticlesFx.light_theta" "Light Direction" + "STD_iwa_TiledParticlesFx.light_phi" "Light Tilt" + + "STD_iwa_TiledParticlesFx.source_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.bright_thres" "Threshold" + "STD_iwa_TiledParticlesFx.center" "Center" + "STD_iwa_TiledParticlesFx.length" "Width" + "STD_iwa_TiledParticlesFx.height" "Height" + "STD_iwa_TiledParticlesFx.animation" "Animation" + "STD_iwa_TiledParticlesFx.step" "Animation Step" + "STD_iwa_TiledParticlesFx.starting_frame" "Starting Frame" + "STD_iwa_TiledParticlesFx.birth_rate" "Birth Rate" + "STD_iwa_TiledParticlesFx.random_seed" "Random Seed" + "STD_iwa_TiledParticlesFx.lifetime" "Lifetime" + "STD_iwa_TiledParticlesFx.lifetime_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.column_lifetime" "Use Column Duration for Lifetime" + "STD_iwa_TiledParticlesFx.gravity" "Gravity" + "STD_iwa_TiledParticlesFx.gravity_angle" "Gravity Angle" + "STD_iwa_TiledParticlesFx.gravityBufferFrame" "Gravity Buffer Frame" + "STD_iwa_TiledParticlesFx.gravity_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.friction" "Friction" + "STD_iwa_TiledParticlesFx.friction_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.wind" "Wind Intensity" + "STD_iwa_TiledParticlesFx.wind_angle" "Wing Angle" + "STD_iwa_TiledParticlesFx.swing_mode" "Swing Mode" + "STD_iwa_TiledParticlesFx.scattering_x" "Horizontal" + "STD_iwa_TiledParticlesFx.scattering_y" "Vertical" + "STD_iwa_TiledParticlesFx.scattering_x_ctrl" "H Control Image" + "STD_iwa_TiledParticlesFx.scattering_y_ctrl" "V Control Image" + "STD_iwa_TiledParticlesFx.swing" "Swing" + "STD_iwa_TiledParticlesFx.speed" "Speed" + "STD_iwa_TiledParticlesFx.speed_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.speed_angle" "Speed Angle" + + "STD_iwa_TiledParticlesFx.speeda_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.speeda_use_gradient" "Use Gradient Angle" + + "STD_iwa_TiledParticlesFx.speed_size" "Linked to Scale" + "STD_iwa_TiledParticlesFx.top_layer" "Top Layer" + "STD_iwa_TiledParticlesFx.mass" "Mass" + "STD_iwa_TiledParticlesFx.scale" "Size" + "STD_iwa_TiledParticlesFx.scale_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.scale_ctrl_all" "Use Control Image for the Whole Lifetime" + "STD_iwa_TiledParticlesFx.rot" "Orientation" + "STD_iwa_TiledParticlesFx.rot_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.trail" "Trail" + "STD_iwa_TiledParticlesFx.trail_step" "Step" + "STD_iwa_TiledParticlesFx.spin_swing_mode" "Swing Mode" + "STD_iwa_TiledParticlesFx.spin_speed" "Rotation Speed" + "STD_iwa_TiledParticlesFx.spin_random" "Extra Speed" + "STD_iwa_TiledParticlesFx.spin_swing" "Rotation Swing" + "STD_iwa_TiledParticlesFx.path_aim" "Follow Particles Movement" + "STD_iwa_TiledParticlesFx.opacity" "Opacity" + "STD_iwa_TiledParticlesFx.opacity_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.trail_opacity" "Trail Opacity" + "STD_iwa_TiledParticlesFx.scale_step" "Size Intensity" + "STD_iwa_TiledParticlesFx.scale_step_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.fade_in" "Fade-in Frames" + "STD_iwa_TiledParticlesFx.fade_out" "Fade-out Frames" + "STD_iwa_TiledParticlesFx.birth_color" "Birth Color" + "STD_iwa_TiledParticlesFx.birth_color_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.birth_color_spread" "Birth Spread" + "STD_iwa_TiledParticlesFx.birth_color_fade" "Birth Intensity" + "STD_iwa_TiledParticlesFx.fadein_color" "Fade-in Color" + "STD_iwa_TiledParticlesFx.fadein_color_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.fadein_color_spread" "Fade-in Spread" + "STD_iwa_TiledParticlesFx.fadein_color_range" "Frame Range" + "STD_iwa_TiledParticlesFx.fadein_color_fade" "Fade-in Intensity" + "STD_iwa_TiledParticlesFx.fadeout_color" "Fade-out Color" + "STD_iwa_TiledParticlesFx.fadeout_color_ctrl" "Control Image" + "STD_iwa_TiledParticlesFx.fadeout_color_spread" "Fade-out Spread" + "STD_iwa_TiledParticlesFx.fadeout_color_range" "Frame Range" + "STD_iwa_TiledParticlesFx.fadeout_color_fade" "Fade-out Intensity" + "STD_iwa_TiledParticlesFx.source_gradation" "Use Control Image Gradation" + "STD_iwa_TiledParticlesFx.pick_color_for_every_frame" "Pick Control Image's Color for Every Frame" + + diff --git a/stuff/config/export-toonz.lua b/stuff/config/export-toonz.lua new file mode 100644 index 0000000..04c341e --- /dev/null +++ b/stuff/config/export-toonz.lua @@ -0,0 +1,89 @@ +export = {}; -- create a new export object + +-- return label +function export:label() + return "Toonz"; +end + +-- return category +function export:category() + return "2D"; +end + +-- return argument list +function export:options() + groups = {}; + for a,actor in magpie.getactors() do + for g,group in magpie.getgroups(actor) do + table.insert(groups, group); + end + end + return { + {"group", "Group", "choice", table.concat(groups, "|")}, + {"toonz_output_file", "Output File", "output_file", "Text files (*.tls)\tAll files (*.*)"} + }; +end + +-- perform the export +function export:run(from, to, options) + + -- open output file + fd = io.open(options.toonz_output_file, "wt"); + if (fd == nil) then + return string.format("could not open '%s'", options.toonz_output_file); + end + + -- write header line to file + fd:write("Toonz\n"); + + -- create an array of all the poses that are being exported + line = ""; + poses = magpie.getposes(options.group); + for p,pose in poses do + line = string.gsub(pose, "[^%.]+%.", ""); + fd:write(line, "\n"); + line = ""; + end + + -- write data to file + for frame = from, to do + line = ""; + + line = frame + magpie.getframeoffset(); + + k = nil; + k = magpie.getgroupvalue(frame, options.group); + if (k ~= nil) then + k = string.gsub(k, "[^%.]+%.", ""); -- remove actor and group name from string + end + if (k == nil) then + k = ""; + end + + if (line ~= "") then + line = line .. "|"; + end + line = line .. k; + + if (line ~= "") then + line = line .. "|"; + end + + comment = magpie.getframecomment(frame); + if (comment ~= "") then + is_empty = false; + else + comment = ""; + end + line = line .. comment; + + fd:write(line, "\n"); + + -- update progress bar in main window + magpie.progress("Exporting...", (frame - from) / (to - from)); + end + + magpie.progress("", 0); -- close progress bar + + fd:close(); -- close output file +end diff --git a/stuff/config/fdg/fld_10_4mm_acme.fdg b/stuff/config/fdg/fld_10_4mm_acme.fdg new file mode 100644 index 0000000..642363c --- /dev/null +++ b/stuff/config/fdg/fld_10_4mm_acme.fdg @@ -0,0 +1,42 @@ +# +# 10 field Toonz field guide (4 mm from ctr hole to fdg edge), Acme peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 99.5 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/fld_12_4mm_acme.fdg b/stuff/config/fdg/fld_12_4mm_acme.fdg new file mode 100644 index 0000000..62bb111 --- /dev/null +++ b/stuff/config/fdg/fld_12_4mm_acme.fdg @@ -0,0 +1,42 @@ +# +# 12 field Toonz field guide (4 mm from ctr hole to fdg edge), Acme peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 118.0 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/fld_12_acme.fdg b/stuff/config/fdg/fld_12_acme.fdg new file mode 100644 index 0000000..796a39b --- /dev/null +++ b/stuff/config/fdg/fld_12_acme.fdg @@ -0,0 +1,42 @@ +# +# 12 field animation field guide, Acme peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 133.2 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/fld_12_oxbry.fdg b/stuff/config/fdg/fld_12_oxbry.fdg new file mode 100644 index 0000000..9eac4b8 --- /dev/null +++ b/stuff/config/fdg/fld_12_oxbry.fdg @@ -0,0 +1,42 @@ +# +# 12 field animation field guide, Oxberry peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 252 + Y0 57 + X1 258 + Y1 70 + X 254.8 + Y 63.5 + XSIZE 6 + YSIZE 13 + AREA 79 + END + HOLE 1 + X0 252 + Y0 162 + X1 258 + Y1 168 + X 254.8 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 24 + END + HOLE 2 + X0 252 + Y0 260 + X1 258 + Y1 273 + X 254.8 + Y 266.7 + XSIZE 6 + YSIZE 13 + AREA 79 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 133.2 +DIST_CTR_HOLE_TO_EDGE 11.9 + diff --git a/stuff/config/fdg/fld_15_4mm_acme.fdg b/stuff/config/fdg/fld_15_4mm_acme.fdg new file mode 100644 index 0000000..f8734d0 --- /dev/null +++ b/stuff/config/fdg/fld_15_4mm_acme.fdg @@ -0,0 +1,42 @@ +# +# 10 field Toonz field guide (4 mm from ctr hole to fdg edge), Acme peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 136.4 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/fld_16_acme.fdg b/stuff/config/fdg/fld_16_acme.fdg new file mode 100644 index 0000000..42a19c2 --- /dev/null +++ b/stuff/config/fdg/fld_16_acme.fdg @@ -0,0 +1,42 @@ +# +# 16 field animation field guide, Acme peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 172.0 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/fld_16_oxbry.fdg b/stuff/config/fdg/fld_16_oxbry.fdg new file mode 100644 index 0000000..8870568 --- /dev/null +++ b/stuff/config/fdg/fld_16_oxbry.fdg @@ -0,0 +1,42 @@ +# +# 16 field animation field guide, Oxberry peg holes +# +VERSION_1.0 +# + HOLE 0 + X0 252 + Y0 57 + X1 258 + Y1 70 + X 254.8 + Y 63.5 + XSIZE 6 + YSIZE 13 + AREA 79 + END + HOLE 1 + X0 252 + Y0 162 + X1 258 + Y1 168 + X 254.8 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 24 + END + HOLE 2 + X0 252 + Y0 260 + X1 258 + Y1 273 + X 254.8 + Y 266.7 + XSIZE 6 + YSIZE 13 + AREA 79 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 172.0 +DIST_CTR_HOLE_TO_EDGE 11.9 + diff --git a/stuff/config/fdg/jpn_cel_film.fdg b/stuff/config/fdg/jpn_cel_film.fdg new file mode 100644 index 0000000..e538bb1 --- /dev/null +++ b/stuff/config/fdg/jpn_cel_film.fdg @@ -0,0 +1,42 @@ +# +# Japanese animation field guide - FILM +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 128.9 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/fdg/jpn_cel_tv.fdg b/stuff/config/fdg/jpn_cel_tv.fdg new file mode 100644 index 0000000..ce21522 --- /dev/null +++ b/stuff/config/fdg/jpn_cel_tv.fdg @@ -0,0 +1,42 @@ +# +# Japanese animation field guide - TV +# +VERSION_1.0 +# + HOLE 0 + X0 253 + Y0 56 + X1 256 + Y1 72 + X 254.0 + Y 63.7 + XSIZE 3 + YSIZE 16 + AREA 49 + END + HOLE 1 + X0 251 + Y0 162 + X1 257 + Y1 168 + X 254.0 + Y 165.1 + XSIZE 6 + YSIZE 6 + AREA 32 + END + HOLE 2 + X0 253 + Y0 259 + X1 256 + Y1 274 + X 254.0 + Y 266.5 + XSIZE 3 + YSIZE 16 + AREA 49 + END +# OTHER INFO +DIST_CTR_TO_CTR_HOLE 117.6 +DIST_CTR_HOLE_TO_EDGE 12.7 + diff --git a/stuff/config/loc/french/colorfx.qm b/stuff/config/loc/french/colorfx.qm new file mode 100644 index 0000000..63480bb Binary files /dev/null and b/stuff/config/loc/french/colorfx.qm differ diff --git a/stuff/config/loc/french/tnzcore.qm b/stuff/config/loc/french/tnzcore.qm new file mode 100644 index 0000000..172b2b9 Binary files /dev/null and b/stuff/config/loc/french/tnzcore.qm differ diff --git a/stuff/config/loc/french/tnztools.qm b/stuff/config/loc/french/tnztools.qm new file mode 100644 index 0000000..9593418 Binary files /dev/null and b/stuff/config/loc/french/tnztools.qm differ diff --git a/stuff/config/loc/french/toonz.qm b/stuff/config/loc/french/toonz.qm new file mode 100644 index 0000000..eae1f90 Binary files /dev/null and b/stuff/config/loc/french/toonz.qm differ diff --git a/stuff/config/loc/french/toonzlib.qm b/stuff/config/loc/french/toonzlib.qm new file mode 100644 index 0000000..1e15488 Binary files /dev/null and b/stuff/config/loc/french/toonzlib.qm differ diff --git a/stuff/config/loc/french/toonzqt.qm b/stuff/config/loc/french/toonzqt.qm new file mode 100644 index 0000000..347533d Binary files /dev/null and b/stuff/config/loc/french/toonzqt.qm differ diff --git a/stuff/config/loc/italian/colorfx.qm b/stuff/config/loc/italian/colorfx.qm new file mode 100644 index 0000000..c881418 Binary files /dev/null and b/stuff/config/loc/italian/colorfx.qm differ diff --git a/stuff/config/loc/italian/tnzcore.qm b/stuff/config/loc/italian/tnzcore.qm new file mode 100644 index 0000000..aaa6eb0 Binary files /dev/null and b/stuff/config/loc/italian/tnzcore.qm differ diff --git a/stuff/config/loc/italian/tnztools.qm b/stuff/config/loc/italian/tnztools.qm new file mode 100644 index 0000000..5e117e5 Binary files /dev/null and b/stuff/config/loc/italian/tnztools.qm differ diff --git a/stuff/config/loc/italian/toonz.qm b/stuff/config/loc/italian/toonz.qm new file mode 100644 index 0000000..3aaeb09 Binary files /dev/null and b/stuff/config/loc/italian/toonz.qm differ diff --git a/stuff/config/loc/italian/toonzlib.qm b/stuff/config/loc/italian/toonzlib.qm new file mode 100644 index 0000000..b9dc54c Binary files /dev/null and b/stuff/config/loc/italian/toonzlib.qm differ diff --git a/stuff/config/loc/italian/toonzqt.qm b/stuff/config/loc/italian/toonzqt.qm new file mode 100644 index 0000000..1567779 Binary files /dev/null and b/stuff/config/loc/italian/toonzqt.qm differ diff --git a/stuff/config/loc/japanese/colorfx.qm b/stuff/config/loc/japanese/colorfx.qm new file mode 100644 index 0000000..86f39c0 Binary files /dev/null and b/stuff/config/loc/japanese/colorfx.qm differ diff --git a/stuff/config/loc/japanese/tnzcore.qm b/stuff/config/loc/japanese/tnzcore.qm new file mode 100644 index 0000000..1532d0a Binary files /dev/null and b/stuff/config/loc/japanese/tnzcore.qm differ diff --git a/stuff/config/loc/japanese/tnztools.qm b/stuff/config/loc/japanese/tnztools.qm new file mode 100644 index 0000000..b780141 Binary files /dev/null and b/stuff/config/loc/japanese/tnztools.qm differ diff --git a/stuff/config/loc/japanese/toonz.qm b/stuff/config/loc/japanese/toonz.qm new file mode 100644 index 0000000..f456cb2 Binary files /dev/null and b/stuff/config/loc/japanese/toonz.qm differ diff --git a/stuff/config/loc/japanese/toonzlib.qm b/stuff/config/loc/japanese/toonzlib.qm new file mode 100644 index 0000000..dd7cd5f Binary files /dev/null and b/stuff/config/loc/japanese/toonzlib.qm differ diff --git a/stuff/config/loc/japanese/toonzqt.qm b/stuff/config/loc/japanese/toonzqt.qm new file mode 100644 index 0000000..2f8eccd Binary files /dev/null and b/stuff/config/loc/japanese/toonzqt.qm differ diff --git a/stuff/config/pap/a3.pap b/stuff/config/pap/a3.pap new file mode 100644 index 0000000..28bfa44 --- /dev/null +++ b/stuff/config/pap/a3.pap @@ -0,0 +1,7 @@ +# +# A3 paper +# +VERSION_1.0 +# +WIDTH 297.00 +LENGTH 420.00 diff --git a/stuff/config/pap/a4.pap b/stuff/config/pap/a4.pap new file mode 100644 index 0000000..9467397 --- /dev/null +++ b/stuff/config/pap/a4.pap @@ -0,0 +1,7 @@ +# +# A4 paper +# +VERSION_1.0 +# +WIDTH 210.00 +LENGTH 297.00 diff --git a/stuff/config/pap/b4.pap b/stuff/config/pap/b4.pap new file mode 100644 index 0000000..c76e050 --- /dev/null +++ b/stuff/config/pap/b4.pap @@ -0,0 +1,7 @@ +# +# B4 paper +# +VERSION_1.0 +# +WIDTH 250.00 +LENGTH 353.00 diff --git a/stuff/config/pap/fld_10.pap b/stuff/config/pap/fld_10.pap new file mode 100644 index 0000000..bf2d7f7 --- /dev/null +++ b/stuff/config/pap/fld_10.pap @@ -0,0 +1,7 @@ +# +# 10 field animation paper 9" x 11" +# +VERSION_1.0 +# +WIDTH 228.6 +LENGTH 279.4 diff --git a/stuff/config/pap/fld_12_a.pap b/stuff/config/pap/fld_12_a.pap new file mode 100644 index 0000000..d34be77 --- /dev/null +++ b/stuff/config/pap/fld_12_a.pap @@ -0,0 +1,7 @@ +# +# 12 field animation paper 10.5" x 12.5" +# +VERSION_1.0 +# +WIDTH 266.7 +LENGTH 317.5 diff --git a/stuff/config/pap/fld_12_b.pap b/stuff/config/pap/fld_12_b.pap new file mode 100644 index 0000000..5c5dd3d --- /dev/null +++ b/stuff/config/pap/fld_12_b.pap @@ -0,0 +1,7 @@ +# +# 12 field animation paper 10.5" x 13" +# +VERSION_1.0 +# +WIDTH 266.7 +LENGTH 330.2 diff --git a/stuff/config/pap/fld_15.pap b/stuff/config/pap/fld_15.pap new file mode 100644 index 0000000..8355057 --- /dev/null +++ b/stuff/config/pap/fld_15.pap @@ -0,0 +1,7 @@ +# +# 15 field animation paper 12.5" x 16" +# +VERSION_1.0 +# +WIDTH 317.5 +LENGTH 406.4 diff --git a/stuff/config/pap/fld_16_a.pap b/stuff/config/pap/fld_16_a.pap new file mode 100644 index 0000000..f3eaa58 --- /dev/null +++ b/stuff/config/pap/fld_16_a.pap @@ -0,0 +1,7 @@ +# +# 16 field animation paper 13.5" x 16.5" +# +VERSION_1.0 +# +WIDTH 342.9 +LENGTH 419.1 diff --git a/stuff/config/pap/fld_16_b.pap b/stuff/config/pap/fld_16_b.pap new file mode 100644 index 0000000..48035a5 --- /dev/null +++ b/stuff/config/pap/fld_16_b.pap @@ -0,0 +1,7 @@ +# +# 16 field animation paper 13.5" x 17" +# +VERSION_1.0 +# +WIDTH 342.9 +LENGTH 431.8 diff --git a/stuff/config/pap/jpn_cel_film.pap b/stuff/config/pap/jpn_cel_film.pap new file mode 100644 index 0000000..d8591cb --- /dev/null +++ b/stuff/config/pap/jpn_cel_film.pap @@ -0,0 +1,7 @@ +# +# Japanese animation paper - FILM +# +VERSION_1.0 +# +WIDTH 256.50 +LENGTH 304.00 diff --git a/stuff/config/pap/jpn_cel_tv.pap b/stuff/config/pap/jpn_cel_tv.pap new file mode 100644 index 0000000..48cad8f --- /dev/null +++ b/stuff/config/pap/jpn_cel_tv.pap @@ -0,0 +1,7 @@ +# +# Japanese animation paper - TV +# +VERSION_1.0 +# +WIDTH 242.00 +LENGTH 268.00 diff --git a/stuff/config/permissions.xml b/stuff/config/permissions.xml new file mode 100644 index 0000000..90b97ea --- /dev/null +++ b/stuff/config/permissions.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/stuff/config/qss/dark/bottomseparator.png b/stuff/config/qss/dark/bottomseparator.png new file mode 100644 index 0000000..070050b Binary files /dev/null and b/stuff/config/qss/dark/bottomseparator.png differ diff --git a/stuff/config/qss/dark/but.png b/stuff/config/qss/dark/but.png new file mode 100644 index 0000000..f5225fe Binary files /dev/null and b/stuff/config/qss/dark/but.png differ diff --git a/stuff/config/qss/dark/butdis.png b/stuff/config/qss/dark/butdis.png new file mode 100644 index 0000000..5bf7318 Binary files /dev/null and b/stuff/config/qss/dark/butdis.png differ diff --git a/stuff/config/qss/dark/buthover.png b/stuff/config/qss/dark/buthover.png new file mode 100644 index 0000000..49c9030 Binary files /dev/null and b/stuff/config/qss/dark/buthover.png differ diff --git a/stuff/config/qss/dark/chk_chk.png b/stuff/config/qss/dark/chk_chk.png new file mode 100644 index 0000000..c8b9cc4 Binary files /dev/null and b/stuff/config/qss/dark/chk_chk.png differ diff --git a/stuff/config/qss/dark/chk_chk_disabled.png b/stuff/config/qss/dark/chk_chk_disabled.png new file mode 100644 index 0000000..cf08a05 Binary files /dev/null and b/stuff/config/qss/dark/chk_chk_disabled.png differ diff --git a/stuff/config/qss/dark/chk_unchk.png b/stuff/config/qss/dark/chk_unchk.png new file mode 100644 index 0000000..ab9e9b6 Binary files /dev/null and b/stuff/config/qss/dark/chk_unchk.png differ diff --git a/stuff/config/qss/dark/chk_unchk_disabled.png b/stuff/config/qss/dark/chk_unchk_disabled.png new file mode 100644 index 0000000..cfa015d Binary files /dev/null and b/stuff/config/qss/dark/chk_unchk_disabled.png differ diff --git a/stuff/config/qss/dark/click.png b/stuff/config/qss/dark/click.png new file mode 100644 index 0000000..3fb14d6 Binary files /dev/null and b/stuff/config/qss/dark/click.png differ diff --git a/stuff/config/qss/dark/current.png b/stuff/config/qss/dark/current.png new file mode 100644 index 0000000..dca53d5 Binary files /dev/null and b/stuff/config/qss/dark/current.png differ diff --git a/stuff/config/qss/dark/dark.qss b/stuff/config/qss/dark/dark.qss new file mode 100644 index 0000000..26af9ab --- /dev/null +++ b/stuff/config/qss/dark/dark.qss @@ -0,0 +1,1009 @@ +/* TOONZ 6.1 Qt Style Test + Digital Video 2007 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(60,60,60); + background-color: rgb(60,60,60); +} +/*---------------------------------------------------------------------------*/ +TPanel { + qproperty-BGColor: rgb(140,140,140); + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(140,140,140); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: gray; + background-color: rgb(140,140,140); +} +QMenu { + margin: 2px; + background-color: rgb(140,140,140); + border: 1px solid black; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/dark/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +QMenu::item:disabled { + color: rgb(90,90,90); + background-color: rgb(140,140,140); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/dark/chk_unchk.png); +} +QMenu::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QMenu::indicator:checked { + image: url(qss/dark/chk_chk.png); +} +QMenu::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + background-color: rgb(140,140,140); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background: rgb(140,140,140); +} +/*Text disable color*/ +QFrame:disabled { + color: rgb(120,120,120); +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(90,90,90); + background-color: rgb(115,115,115); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(115,115,115); +} +QDialog #dialogMainFrame { + background-color: rgb(140,140,140); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(90,90,90); +} +StyleEditor QToolBar { + border-right: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background: rgb(100,100,100); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(145,145,145); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background: rgb(120,120,120); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(140,140,140); +} +#dvTopBar { + background-color: rgb(115,115,115); +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SpreadsheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135 ); + qproperty-DarkLineColor: rgb(90,90,90); +} +#ScrollArea { + border:1px solid rgb(95,95,95); +} +#CellScrollArea { + border-top:1px solid rgb(95,95,95); + border-left:1px solid rgb(95,95,95); +} +XsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135); + qproperty-DarkLineColor: rgb(90,90,90); +} +#xsheetScrollArea { + background: rgb(140,140,140); + margin: 1px; +} +#cornerWidget { + background: rgb(140,140,140); +} +#XshColumnChangeObjectWidget { + background-color: rgb(155,155,155); + selection-background-color: rgb(49,106,197); + border: 1px solid black +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(140,140,140); + qproperty-ValueLineColor: rgb(160,160,160); + qproperty-FrameLineColor: rgb(150,150,150); + qproperty-OtherCurvesColor: rgb(95,95,95); + qproperty-RulerBackground: rgb(160,160,160); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(140,140,140); + qproperty-LightLineColor: rgb(100,100,100); + qproperty-DarkLineColor: rgb(95,95,95); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(90,90,90); + background-color: rgb(140,140,140); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/dark/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(140,140,140); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar */ +#toolBar { + margin: 1px; + background-color: rgb(140,140,140); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(90,90,90); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(140,140,140); +} +#toolBar QToolButton:pressed { + border-image: url(qss/dark/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/dark/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(140,140,140); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(90,90,90); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(140,140,140); + border: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/dark/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/dark/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/dark/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/dark/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + border-image: url(qss/dark/but.png) 2; + border-width: 2; + color: black; +} +/*---------------------------------------------------------------------------*/ +#PeggingWidget { + background-color: rgb(0,0,0); +} +/*---------------------------------------------------------------------------*/ +#PeggingButton { + border-image: none; + background-color: rgb(228,228,228); + border: 0px solid black; +} +#PeggingButton:hover { + border-image: url(qss/standard/over.png) 2; + border-width: 2; +} +#PeggingButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#PeggingButton:checked { + border-image: none; + background-color: rgb(255,255,255); + border-left-color: lightgray; + border-left-width: 2px; + border-top-color: lightgray; + border-top-width: 2px; + border-right-width: 0px; + border-bottom-width: 0px; +} +/*---------------------------------------------------------------------------*/ +QComboBox[editable="false"] { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/dark/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(90,90,90); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/dark/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; +} +QComboBox[editable="false"]::drop-down { + width: 15px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(160,160,160); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(180,180,180); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +#ScrollLeftButton { + image: url(qss/dark/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/dark/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/dark/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/dark/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/*---------------------------------------------------------------------------*/ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QToolButton:disabled { + border-image: url(qss/dark/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(145,145,145); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/dark/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/dark/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + border-image: url(qss/dark/frame.png) 3; + border-width: 3; +} +QLineEdit::focus { + border-image: url(qss/dark/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(135,135,135); + border-image: url(qss/dark/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit */ +QPlainTextEdit, QTexEdit { + border-image: url(qss/dark/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/dark/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit:disabled { + color: rgb(135,135,135); + border-image: url(qss/dark/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; +} +QGroupBox:enabled +{ + border-color: rgb(90, 90, 90); +} +QGroupBox:disabled +{ + border-color: rgb(140, 140, 140); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/dark/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal:enabled { + image: url(qss/dark/h_handle.png); + width: 16px; + margin: -16px -4px; +} +QSlider::handle:horizontal:disabled { + image: url(qss/dark/h_handle_disabled.png); + width: 16px; + margin: -16px -4px; +} + + +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/dark/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/dark/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/dark/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/dark/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/dark/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/dark/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/dark/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/dark/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/dark/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + qproperty-PBHeight: 10; + + qproperty-PBOverlay: url(qss/dark/flipslider.png); + qproperty-PBMarker: url(qss/dark/flipmarker.png); + + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ + +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/dark/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/dark/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/dark/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/dark/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/dark/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/dark/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(120,120,120); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/dark/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/dark/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/dark/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/dark/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/dark/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/dark/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/dark/sb_hhandle.png)3; + border-width: 3; + image: url(qss/dark/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/dark/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/dark/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/dark/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/dark/sb_vhandle.png)3; + border-width: 3; + image: url(qss/dark/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/dark/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/dark/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/dark/chk_unchk.png); +} +QCheckBox::indicator:unchecked:disabled, QTreeView::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/dark/chk_chk.png); +} +QCheckBox::indicator:checked:disabled, QTreeView::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/dark/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/dark/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(120,120,120); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(120,120,120); +} +QSplitter::handle:horizontal { + image: url(qss/dark/hsplitter_handle.png); + border-left: 1px solid rgb(90,90,90); + border-right: 1px solid rgb(90,90,90); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/dark/vsplitter_handle.png); + border-top: 1px solid rgb(90,90,90); + border-bottom: 1px solid rgb(90,90,90); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(115,115,115); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; + alternate-background-color: rgb(90, 90, 90); +} + diff --git a/stuff/config/qss/dark/dark_mac.qss b/stuff/config/qss/dark/dark_mac.qss new file mode 100644 index 0000000..1ada2e9 --- /dev/null +++ b/stuff/config/qss/dark/dark_mac.qss @@ -0,0 +1,1049 @@ +/* TOONZ 6.1 Qt Style Test + Digital Video 2007 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(60,60,60); + background-color: rgb(60,60,60); +} +/*---------------------------------------------------------------------------*/ +/*------------ Mac OS ------------*/ +TPanel { + qproperty-BGColor: rgb(140,140,140); + background-color: rgb(140,140,140); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-color: rgb(140,140,140); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(140,140,140); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: gray; + background-color: rgb(140,140,140); +} +QMenu { + margin: 2px; + background-color: rgb(140,140,140); + border: 1px solid black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/dark/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +QMenu::item:disabled { + color: rgb(90,90,90); + background-color: rgb(140,140,140); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/dark/chk_unchk.png); +} +QMenu::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QMenu::indicator:checked { + image: url(qss/dark/chk_chk.png); +} +QMenu::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + margin-bottom: -1px; + border-image: url(qss/dark/but.png) 2; + border-width: 2; + color: black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background: rgb(140,140,140); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*Text disable color*/ +QFrame:disabled { + color: rgb(120,120,120); +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(90,90,90); + background-color: rgb(115,115,115); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(115,115,115); +} +QDialog #dialogMainFrame { + background-color: rgb(140,140,140); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(90,90,90); +} +StyleEditor QToolBar { + border-right: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/dark/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background: rgb(100,100,100); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(145,145,145); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background: rgb(120,120,120); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(115,115,115); +} +#dvTopBar { + background-color: rgb(115,115,115); +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SpreadsheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135 ); + qproperty-DarkLineColor: rgb(90,90,90); +} +#ScrollArea { + border:1px solid rgb(95,95,95); +} +#CellScrollArea { + border-top:1px solid rgb(95,95,95); + border-left:1px solid rgb(95,95,95); +} +XsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135); + qproperty-DarkLineColor: rgb(90,90,90); +} +#xsheetScrollArea { + background: rgb(140,140,140); + margin: 1px; +} +#cornerWidget { + background: rgb(140,140,140); +} +#XshColumnChangeObjectWidget { + background-color: rgb(155,155,155); + selection-background-color: rgb(49,106,197); + border: 1px solid black +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(140,140,140); + qproperty-ValueLineColor: rgb(160,160,160); + qproperty-FrameLineColor: rgb(150,150,150); + qproperty-OtherCurvesColor: rgb(95,95,95); + qproperty-RulerBackground: rgb(160,160,160); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(140,140,140); + qproperty-LightLineColor: rgb(100,100,100); + qproperty-DarkLineColor: rgb(95,95,95); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(90,90,90); + background-color: rgb(140,140,140); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/dark/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(140,140,140); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar */ +#toolBar { + margin: 1px; + background-color: rgb(140,140,140); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(90,90,90); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(140,140,140); +} +#toolBar QToolButton:pressed { + border-image: url(qss/dark/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/dark/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + margin: 0px; + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(140,140,140); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(90,90,90); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(140,140,140); + border: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/dark/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/dark/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/dark/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/dark/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + margin: 0px; + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + background-color: rgb(140,140,140); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +#PeggingButton { + border-image: none; + background-color: rgb(228,228,228); + border: 0px solid black; +} +#PeggingButton:hover { + border-image: url(qss/standard/over.png) 2; + border-width: 2; +} +#PeggingButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#PeggingButton:checked { + border-image: none; + background-color: rgb(255,255,255); + border-left-color: lightgray; + border-left-width: 2px; + border-top-color: lightgray; + border-top-width: 2px; + border-right-width: 0px; + border-bottom-width: 0px; +} +/*---------------------------------------------------------------------------*/ +QComboBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +QComboBox[editable="false"] { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/dark/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(90,90,90); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/dark/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; /* space for the arrow */ +} +QComboBox[editable="false"]::drop-down { + width: 15px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(160,160,160); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(180,180,180); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +#ScrollLeftButton { + image: url(qss/dark/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/dark/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/dark/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/dark/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/*---------------------------------------------------------------------------*/ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/dark/but.png) 2; + border-width: 2; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QToolButton:disabled { + border-image: url(qss/dark/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/dark/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/dark/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/dark/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/dark/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(145,145,145); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/dark/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/dark/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + margin-bottom: 1px; + margin-right: 1px; + border-image: url(qss/dark/frame.png) 3; + border-width: 3; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QLineEdit::focus { + border-image: url(qss/dark/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(135,135,135); + border-image: url(qss/dark/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit */ +QPlainTextEdit, QTexEdit { + border-image: url(qss/dark/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/dark/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit:disabled { + color: rgb(135,135,135); + border-image: url(qss/dark/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QGroupBox:enabled +{ + border-color: rgb(90, 90, 90); +} +QGroupBox:disabled +{ + border-color: rgb(140, 140, 140); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/dark/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal:enabled { + image: url(qss/dark/h_handle.png); + width: 16px; + margin: -16px -4px; +} +QSlider::handle:horizontal:disabled { + image: url(qss/dark/h_handle_disabled.png); + width: 16px; + margin: -16px -4px; +} + + +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/dark/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/dark/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/dark/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/dark/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/dark/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/dark/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/dark/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/dark/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/dark/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + qproperty-PBHeight: 10; + + qproperty-PBOverlay: url(qss/dark/flipslider.png); + qproperty-PBMarker: url(qss/dark/flipmarker.png); + + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/dark/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/dark/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/dark/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/dark/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/dark/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/dark/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(120,120,120); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/dark/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/dark/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/dark/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/dark/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/dark/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/dark/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/dark/sb_hhandle.png)3; + border-width: 3; + image: url(qss/dark/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/dark/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/dark/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/dark/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/dark/sb_vhandle.png)3; + border-width: 3; + image: url(qss/dark/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/dark/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/dark/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/dark/chk_unchk.png); +} +QCheckBox::indicator:unchecked:disabled, QTreeView::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/dark/chk_chk.png); +} +QCheckBox::indicator:checked:disabled, QTreeView::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/dark/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/dark/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(120,120,120); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(120,120,120); +} +QSplitter::handle:horizontal { + image: url(qss/dark/hsplitter_handle.png); + border-left: 1px solid rgb(90,90,90); + border-right: 1px solid rgb(90,90,90); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/dark/vsplitter_handle.png); + border-top: 1px solid rgb(90,90,90); + border-bottom: 1px solid rgb(90,90,90); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(115,115,115); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; + alternate-background-color: rgb(90, 90, 90); +} + diff --git a/stuff/config/qss/dark/down_arrow.png b/stuff/config/qss/dark/down_arrow.png new file mode 100644 index 0000000..2bcd46a Binary files /dev/null and b/stuff/config/qss/dark/down_arrow.png differ diff --git a/stuff/config/qss/dark/down_arrow_black.png b/stuff/config/qss/dark/down_arrow_black.png new file mode 100644 index 0000000..d9fbc98 Binary files /dev/null and b/stuff/config/qss/dark/down_arrow_black.png differ diff --git a/stuff/config/qss/dark/down_arrow_disabled.png b/stuff/config/qss/dark/down_arrow_disabled.png new file mode 100644 index 0000000..d9eefed Binary files /dev/null and b/stuff/config/qss/dark/down_arrow_disabled.png differ diff --git a/stuff/config/qss/dark/flipmarker.png b/stuff/config/qss/dark/flipmarker.png new file mode 100644 index 0000000..a35bfd9 Binary files /dev/null and b/stuff/config/qss/dark/flipmarker.png differ diff --git a/stuff/config/qss/dark/flipslider.png b/stuff/config/qss/dark/flipslider.png new file mode 100644 index 0000000..f402f30 Binary files /dev/null and b/stuff/config/qss/dark/flipslider.png differ diff --git a/stuff/config/qss/dark/frame.png b/stuff/config/qss/dark/frame.png new file mode 100644 index 0000000..ad1714f Binary files /dev/null and b/stuff/config/qss/dark/frame.png differ diff --git a/stuff/config/qss/dark/framedisable.png b/stuff/config/qss/dark/framedisable.png new file mode 100644 index 0000000..b150ab4 Binary files /dev/null and b/stuff/config/qss/dark/framedisable.png differ diff --git a/stuff/config/qss/dark/framehover.png b/stuff/config/qss/dark/framehover.png new file mode 100644 index 0000000..f11d77f Binary files /dev/null and b/stuff/config/qss/dark/framehover.png differ diff --git a/stuff/config/qss/dark/ftab.png b/stuff/config/qss/dark/ftab.png new file mode 100644 index 0000000..7f12d27 Binary files /dev/null and b/stuff/config/qss/dark/ftab.png differ diff --git a/stuff/config/qss/dark/ftbtab.png b/stuff/config/qss/dark/ftbtab.png new file mode 100644 index 0000000..52434cd Binary files /dev/null and b/stuff/config/qss/dark/ftbtab.png differ diff --git a/stuff/config/qss/dark/fuptab.png b/stuff/config/qss/dark/fuptab.png new file mode 100644 index 0000000..aefec60 Binary files /dev/null and b/stuff/config/qss/dark/fuptab.png differ diff --git a/stuff/config/qss/dark/fuptbtab.png b/stuff/config/qss/dark/fuptbtab.png new file mode 100644 index 0000000..10661f9 Binary files /dev/null and b/stuff/config/qss/dark/fuptbtab.png differ diff --git a/stuff/config/qss/dark/h_chandle.png b/stuff/config/qss/dark/h_chandle.png new file mode 100644 index 0000000..ec9fb31 Binary files /dev/null and b/stuff/config/qss/dark/h_chandle.png differ diff --git a/stuff/config/qss/dark/h_handle.png b/stuff/config/qss/dark/h_handle.png new file mode 100644 index 0000000..2a2ba59 Binary files /dev/null and b/stuff/config/qss/dark/h_handle.png differ diff --git a/stuff/config/qss/dark/h_handle_disabled.png b/stuff/config/qss/dark/h_handle_disabled.png new file mode 100644 index 0000000..db7ed13 Binary files /dev/null and b/stuff/config/qss/dark/h_handle_disabled.png differ diff --git a/stuff/config/qss/dark/h_slider_left.png b/stuff/config/qss/dark/h_slider_left.png new file mode 100644 index 0000000..1b9e355 Binary files /dev/null and b/stuff/config/qss/dark/h_slider_left.png differ diff --git a/stuff/config/qss/dark/h_slider_left_disabled.png b/stuff/config/qss/dark/h_slider_left_disabled.png new file mode 100644 index 0000000..8b97fe3 Binary files /dev/null and b/stuff/config/qss/dark/h_slider_left_disabled.png differ diff --git a/stuff/config/qss/dark/h_slider_right.png b/stuff/config/qss/dark/h_slider_right.png new file mode 100644 index 0000000..2276512 Binary files /dev/null and b/stuff/config/qss/dark/h_slider_right.png differ diff --git a/stuff/config/qss/dark/h_slider_right_disabled.png b/stuff/config/qss/dark/h_slider_right_disabled.png new file mode 100644 index 0000000..5d97d51 Binary files /dev/null and b/stuff/config/qss/dark/h_slider_right_disabled.png differ diff --git a/stuff/config/qss/dark/hsplitter_handle.png b/stuff/config/qss/dark/hsplitter_handle.png new file mode 100644 index 0000000..2893eb9 Binary files /dev/null and b/stuff/config/qss/dark/hsplitter_handle.png differ diff --git a/stuff/config/qss/dark/left_arrow_black.png b/stuff/config/qss/dark/left_arrow_black.png new file mode 100644 index 0000000..b89cdcc Binary files /dev/null and b/stuff/config/qss/dark/left_arrow_black.png differ diff --git a/stuff/config/qss/dark/ltab.png b/stuff/config/qss/dark/ltab.png new file mode 100644 index 0000000..6e1da97 Binary files /dev/null and b/stuff/config/qss/dark/ltab.png differ diff --git a/stuff/config/qss/dark/ltbtab.png b/stuff/config/qss/dark/ltbtab.png new file mode 100644 index 0000000..f99cb00 Binary files /dev/null and b/stuff/config/qss/dark/ltbtab.png differ diff --git a/stuff/config/qss/dark/menuseparator.PNG b/stuff/config/qss/dark/menuseparator.PNG new file mode 100644 index 0000000..8f3bc2b Binary files /dev/null and b/stuff/config/qss/dark/menuseparator.PNG differ diff --git a/stuff/config/qss/dark/oouptab.png b/stuff/config/qss/dark/oouptab.png new file mode 100644 index 0000000..6faf3fb Binary files /dev/null and b/stuff/config/qss/dark/oouptab.png differ diff --git a/stuff/config/qss/dark/over.png b/stuff/config/qss/dark/over.png new file mode 100644 index 0000000..5ea84de Binary files /dev/null and b/stuff/config/qss/dark/over.png differ diff --git a/stuff/config/qss/dark/radio.png b/stuff/config/qss/dark/radio.png new file mode 100644 index 0000000..ef901a8 Binary files /dev/null and b/stuff/config/qss/dark/radio.png differ diff --git a/stuff/config/qss/dark/radio_p.png b/stuff/config/qss/dark/radio_p.png new file mode 100644 index 0000000..277940f Binary files /dev/null and b/stuff/config/qss/dark/radio_p.png differ diff --git a/stuff/config/qss/dark/right_arrow_black.png b/stuff/config/qss/dark/right_arrow_black.png new file mode 100644 index 0000000..8878c62 Binary files /dev/null and b/stuff/config/qss/dark/right_arrow_black.png differ diff --git a/stuff/config/qss/dark/sb_downarrow.png b/stuff/config/qss/dark/sb_downarrow.png new file mode 100644 index 0000000..bb82321 Binary files /dev/null and b/stuff/config/qss/dark/sb_downarrow.png differ diff --git a/stuff/config/qss/dark/sb_h.png b/stuff/config/qss/dark/sb_h.png new file mode 100644 index 0000000..741221f Binary files /dev/null and b/stuff/config/qss/dark/sb_h.png differ diff --git a/stuff/config/qss/dark/sb_hhandle.png b/stuff/config/qss/dark/sb_hhandle.png new file mode 100644 index 0000000..2553dce Binary files /dev/null and b/stuff/config/qss/dark/sb_hhandle.png differ diff --git a/stuff/config/qss/dark/sb_hline.png b/stuff/config/qss/dark/sb_hline.png new file mode 100644 index 0000000..2eb8d10 Binary files /dev/null and b/stuff/config/qss/dark/sb_hline.png differ diff --git a/stuff/config/qss/dark/sb_larrow.png b/stuff/config/qss/dark/sb_larrow.png new file mode 100644 index 0000000..856b246 Binary files /dev/null and b/stuff/config/qss/dark/sb_larrow.png differ diff --git a/stuff/config/qss/dark/sb_rarrow.png b/stuff/config/qss/dark/sb_rarrow.png new file mode 100644 index 0000000..e7a2d35 Binary files /dev/null and b/stuff/config/qss/dark/sb_rarrow.png differ diff --git a/stuff/config/qss/dark/sb_uparrow.png b/stuff/config/qss/dark/sb_uparrow.png new file mode 100644 index 0000000..6645fca Binary files /dev/null and b/stuff/config/qss/dark/sb_uparrow.png differ diff --git a/stuff/config/qss/dark/sb_v.png b/stuff/config/qss/dark/sb_v.png new file mode 100644 index 0000000..46a68fb Binary files /dev/null and b/stuff/config/qss/dark/sb_v.png differ diff --git a/stuff/config/qss/dark/sb_vhandle.png b/stuff/config/qss/dark/sb_vhandle.png new file mode 100644 index 0000000..5caea11 Binary files /dev/null and b/stuff/config/qss/dark/sb_vhandle.png differ diff --git a/stuff/config/qss/dark/sb_vline.png b/stuff/config/qss/dark/sb_vline.png new file mode 100644 index 0000000..3633774 Binary files /dev/null and b/stuff/config/qss/dark/sb_vline.png differ diff --git a/stuff/config/qss/dark/sl_groove.png b/stuff/config/qss/dark/sl_groove.png new file mode 100644 index 0000000..951b253 Binary files /dev/null and b/stuff/config/qss/dark/sl_groove.png differ diff --git a/stuff/config/qss/dark/tab.png b/stuff/config/qss/dark/tab.png new file mode 100644 index 0000000..4f41d89 Binary files /dev/null and b/stuff/config/qss/dark/tab.png differ diff --git a/stuff/config/qss/dark/tbtab.png b/stuff/config/qss/dark/tbtab.png new file mode 100644 index 0000000..2ba38e7 Binary files /dev/null and b/stuff/config/qss/dark/tbtab.png differ diff --git a/stuff/config/qss/dark/titlebar_horizontal.png b/stuff/config/qss/dark/titlebar_horizontal.png new file mode 100644 index 0000000..62a1fbc Binary files /dev/null and b/stuff/config/qss/dark/titlebar_horizontal.png differ diff --git a/stuff/config/qss/dark/titlebar_vertical.png b/stuff/config/qss/dark/titlebar_vertical.png new file mode 100644 index 0000000..eacef0d Binary files /dev/null and b/stuff/config/qss/dark/titlebar_vertical.png differ diff --git a/stuff/config/qss/dark/titlebar_vertical_inactive.png b/stuff/config/qss/dark/titlebar_vertical_inactive.png new file mode 100644 index 0000000..5564aca Binary files /dev/null and b/stuff/config/qss/dark/titlebar_vertical_inactive.png differ diff --git a/stuff/config/qss/dark/up_arrow.png b/stuff/config/qss/dark/up_arrow.png new file mode 100644 index 0000000..142e60d Binary files /dev/null and b/stuff/config/qss/dark/up_arrow.png differ diff --git a/stuff/config/qss/dark/up_arrow_black.png b/stuff/config/qss/dark/up_arrow_black.png new file mode 100644 index 0000000..6c27909 Binary files /dev/null and b/stuff/config/qss/dark/up_arrow_black.png differ diff --git a/stuff/config/qss/dark/up_arrow_disabled.png b/stuff/config/qss/dark/up_arrow_disabled.png new file mode 100644 index 0000000..4d2c277 Binary files /dev/null and b/stuff/config/qss/dark/up_arrow_disabled.png differ diff --git a/stuff/config/qss/dark/uptab.png b/stuff/config/qss/dark/uptab.png new file mode 100644 index 0000000..b470ef8 Binary files /dev/null and b/stuff/config/qss/dark/uptab.png differ diff --git a/stuff/config/qss/dark/uptbtab.png b/stuff/config/qss/dark/uptbtab.png new file mode 100644 index 0000000..5fe4c9d Binary files /dev/null and b/stuff/config/qss/dark/uptbtab.png differ diff --git a/stuff/config/qss/dark/vsplitter_handle.png b/stuff/config/qss/dark/vsplitter_handle.png new file mode 100644 index 0000000..aae6b9a Binary files /dev/null and b/stuff/config/qss/dark/vsplitter_handle.png differ diff --git a/stuff/config/qss/gray_048/gray_048.less b/stuff/config/qss/gray_048/gray_048.less new file mode 100644 index 0000000..ce17b29 --- /dev/null +++ b/stuff/config/qss/gray_048/gray_048.less @@ -0,0 +1,1299 @@ + +/* LESS Definitions */ + +/*Image URL*/ +@base_url: "qss/gray_072"; +@image_url: "@{base_url}/imgs"; + +/*Text Color*/ +@m_baseTxtColor: rgb(230,230,230); +@m_disabledTxtColor: rgb(128,128,128); + +@m_baseBG: rgb(48,48,48); +@m_base_lightH: rgb(88,88,88); +@m_base_lightV: rgb(108,108,108); +@m_base_darkH: rgb(12,12,12); +@m_base_darkV: rgb(0,0,0); + +/*Used in Dialog border*/ +@m_dialog_border_color: rgb(0,0,0); + +/*Color for Selected Item*/ +@m_selectedBG: rgb(128,160,220); + +/*Color for title texts*/ +@m_titleTxtColor: lighten(@m_selectedBG, 10%); + +/* color adjustable by delta */ +.baseBG(@dark: 0%){ + background-color: darken(@m_baseBG, @dark); +} +.baseBG(light, @light: 0%){ + background-color: lighten(@m_baseBG, @light); +} + +.base_inset(@dark: 0%){ + .baseBG(@dark); + border-style: inset; + border-left-color: darken(@m_base_darkH, @dark); + border-top-color: darken(@m_base_darkV, @dark); + border-right-color: darken(@m_base_lightH, @dark); + border-bottom-color: darken(@m_base_lightV, @dark); +} +.base_inset(light, @light: 0%){ + .baseBG(light, @light); + border-style: inset; + border-left-color: lighten(@m_base_darkH, @light); + border-top-color: lighten(@m_base_darkV, @light); + border-right-color: lighten(@m_base_lightH, @light); + border-bottom-color: lighten(@m_base_lightV, @light); +} + +.base_outset(@dark: 0%){ + .baseBG(@dark); + border-style: outset; + border-left-color: darken(@m_base_lightH, @dark); + border-top-color: darken(@m_base_lightV, @dark); + border-right-color: darken(@m_base_darkH, @dark); + border-bottom-color: darken(@m_base_darkV, @dark); +} +.base_outset(light, @light: 0%){ + .baseBG(light, @light); + border-style: outset; + border-left-color: lighten(@m_base_lightH, @light); + border-top-color: lighten(@m_base_lightV, @light); + border-right-color: lighten(@m_base_darkH, @light); + border-bottom-color: lighten(@m_base_darkV, @light); +} + +/*set padding*/ +.set_padding(@hPad: 0px, @vPad: 0px){ + padding-left: @hPad; + padding-right: @hPad; + padding-top: @vPad; + padding-bottom: @vPad; +} +/*set margin*/ +.set_margin(@hMgn: 0px, @vMgn: 0px) { + margin-left: @hMgn; + margin-right: @hMgn; + margin-top: @vMgn; + margin-bottom: @vMgn; +} + +/* ------ Qt Widgets Common Difinitions ------ */ + +QWidget { + color: @m_baseTxtColor; + .baseBG; +} + +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} + +QDialog +{ + .baseBG; +} + +QMainWindow::separator +{ + background: yellow; + width: 10px; /* when vertical */ + height: 10px; /* when horizontal */ +} + +QToolTip, #helpTooltip +{ + border: 1px solid black; + background-color: rgb(255,255,225); + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + .base_inset; + alternate-background-color: lighten(@m_baseBG, 5%); + + &::item:selected + { + background-color: @m_selectedBG; + color: black; + } + &::item + { + color: @m_baseTxtColor; + } + +} +QStatusBar { + background-color: rgb(192,192,192); + + &::item { + border-width: 0; + } + & QLabel { + background-color: rgb(192,192,192); + } + & #StatusBarLabel { + background-color: rgb(255,255,255); + .set_padding( 3px, 1px ); + } +} +QMenuBar +{ + .baseBG(5%); + &::item:selected{ + .base_inset(5%); + border-width: 1px; + } +} + +QMenu +{ + .baseBG(5%); + + &::item { + &:selected{ + background: @m_selectedBG; + color: black; + } + &:disabled{ + .baseBG(light, 5%); + color: @m_disabledTxtColor; + } + &:disabled:selected{ + background: rgb(108,118,128); + } + } + + &::separator { + .base_inset(10%); + .set_margin(5px,2px); + border-width: 1px; + height: 0px; + } +} + +QToolBar +{ + .base_outset; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; + + &::separator:horizontal { + image: url("@{image_url}/bottomseparator.png"); + } + &::separator:vertical { + image: url("@{image_url}/separator.png"); + } + + & QToolButton { + .baseBG; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; + &:hover { + border-image: url("@{image_url}/over.png") 2; + } + &:checked, + &:pressed { + border-image: url("@{image_url}/click.png") 2; + } + &:disabled{ + .baseBG(light, 5%); + color: @m_disabledTxtColor; + } + &::menu-indicator + { + image: none; + } + &::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ + } + } + + & QLabel + { + .baseBG; + margin-top: 1px; + border-width: 2; + } + + & QToolBar + { + border-width: 0px; + } +} + +QLineEdit { + /*darken little bit*/ + .base_inset(10%); + border-width: 1px; + border-radius: 2px; + &:disabled { + .base_inset(light, 10%); + color: @m_disabledTxtColor; + } +} +QComboBox { + /*darken little bit*/ + .base_inset(10%); + border-width: 1px; + .set_padding( 3px, 0px ); + + /*arrow button*/ + &::drop-down { + .base_outset; + border-width: 2px; + /*pressed state*/ + &:on { + .base_inset; + } + } + /*arrow button triangle*/ + &::down-arrow { + image: url("@{image_url}/combo_down_arrow.png"); + } + &:disabled { + .base_inset(light, 10%); + color: @m_disabledTxtColor; + } + +} + +QPushButton { + .base_outset; + border-width: 1px; + border-radius: 4px; + .set_padding(20px, 3px); + + /*lighten lilttle bit when hover*/ + &:hover { + .base_outset(light, 10%); + &:pressed { + .base_inset(light, 10%); + } + } + /*lighten lilttle bit when pressed*/ + &:checked { + .base_inset(light, 10%); + } + &:disabled{ + .base_outset(light, 5%); + color: rgb(80,80,80); + } +} + +#PushButton_NoPadding { + .set_padding(3px, 3px); +} + +QCheckBox { + &:hover { + .baseBG(light, 10%); + } + &:disabled { + color: @m_disabledTxtColor; + } + &::indicator { + .base_inset(10%); + border-width: 2px; + &:disabled { + .base_inset(light, 5%); + } + &:checked { + image: url("@{image_url}/check_indicator.png"); + &:disabled { + image: url("@{image_url}/check_indicator_disabled.png"); + } + } + } +} + +QSlider { + &::groove:horizontal { + .base_inset(10%); + border-width: 1px; + height: 1px; + margin: 1px; + } + &::handle:horizontal { + .base_outset(light, 10%); + border-width: 2px; + width: 5px; + margin: -8px 0px; /* expand outside the groove */ + } +} + +QGroupBox { + border: 1px solid @m_baseTxtColor; + .set_margin( 5px, 5px ); + .set_padding( 3px, 5px ); + &::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; + } +} + +QSplitter::handle +{ + background-color: lighten(@m_baseBG, 25); +} + +/* ------ Toonz Classes Difinitions ------ */ + +TPanel { + /*Used for dialog border*/ + background-color: @m_dialog_border_color; +} + +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer +{ + margin: 0px; + padding: 0px; + & QToolBar + { + border: 1px; + .base_outset(); + & QToolButton + { + margin: 0px; + padding: 1px; + border: 0px; + } + } + & #keyFrameNavigator + { + border: 0px; + } +} +#TabBarContainer{ + .baseBG(15%); + & #ScrollLeftButton, + & #ScrollRightButton{ + margin-top: 1px; + } +} +#PaletteTabBar, +#FxSettingsTabBar{ + .baseBG(15%); + &::tab { + .set_padding( 7px, 2px ); + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + .base_outset(5%); /* for non selected tab */ + margin-top: 2px; /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset(); + margin-top: 0px; + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; + } + + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#PaletteLockButton{ + &:hover{ + border-image: url("@{image_url}/over_yellow.png") 2; + } + &:checked{ + border-image: url("@{image_url}/click_pink.png") 2; + &:hover{ + border-image: url("@{image_url}/over_pressed_yellow.png") 2; + } + } +} + +#PageViewer{ + qproperty-TextColor: @m_baseTxtColor; +} + +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + .baseBG(15%); + &::tab{ + .set_padding( 2px, 1px ); + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + .base_outset(5%); /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset; + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; + } + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#HexagonalColorWheel { + qproperty-BGColor: @m_baseBG; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider { + &::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; + } + &::handle:horizontal { + width: 8px; + margin: -8px -4px; + } +} + +#colorSliderAddButton, +#colorSliderSubButton +{ + border-image: url("@{image_url}/colorslider_button_bg.png")2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} + +#colorSliderAddButton +{ + image: url("@{image_url}/colorslider_add.png"); + &:pressed { + image: url("@{image_url}/colorslider_add_pressed.png"); + } +} + +#colorSliderSubButton +{ + image: url("@{image_url}/colorslider_sub.png"); + &:pressed { + image: url("@{image_url}/colorslider_sub_pressed.png"); + } +} + +#PlainColorPageParts +{ + .base_outset; + border-top-width: 1px; + border-bottom-width: 1px; +} + +#colorSliderLabel, +#colorSliderField +{ + font-size: 14px; +} + + +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: rgb(225,225,225); + + &:hover { + background-color: rgb(245,245,245); + } + &:pressed { + background-color: rgb(215,215,215); + } +} + +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} + +#ScrollLeftButton { + image: url("@{image_url}/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("@{image_url}/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("@{image_url}/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("@{image_url}/down_arrow_black.png"); + border-top: 1px solid black; +} + +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + .baseBG(15%); + + & #ToolBarContainer + { + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 3px; + } +} + +FlipBook #ToolBarContainer +{ + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 3px; +} + +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} + +#ViewerFpsSlider +{ + .baseBG(light, 10%); + .set_margin(19px, 0px); + border: 1px solid black; + height: 21px; + + &::handle { + border-image: url("@{image_url}/handle_border.png")6; + border-width: 6px; + image: none; + min-width: 5px; + } + &::add-line { + image: url("@{image_url}/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_rarrow_pressed.png"); + } + } + &::sub-line { + image: url("@{image_url}/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_larrow_pressed.png"); + } + } +} + +#FlipConsolePlayToolBar{ + border: none; + & QToolButton { + height: 14px; + } +} + +FlipSlider { + qproperty-PBHeight: 20; + + qproperty-PBOverlay: url("@{image_url}/flipslider.png"); + qproperty-PBMarker: url("@{image_url}/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-baseColor: #626262; + qproperty-notStartedColor: rgb(204,34,34); + qproperty-startedColor: rgb(200,128,128); + qproperty-baseColor: #626262; +} + +Ruler { + qproperty-ParentBGColor: rgb(48,48,48); + qproperty-ScaleColor: rgb(230,230,230); +} + +#ComboViewerToolOptions{ + border: 1px; + .base_outset; +} +#RulerToolOptionValues{ + color: black; +} + +/*-----------File Browser------------*/ +#DirTreeView, #FunctionEditorTree, #ShortcutTree, #FxTreeView +{ + alternate-background-color: lighten(@m_baseBG, 5%); + border-width: 1px; + .base_inset; + margin: 0px; +} +#DirTreeView::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree_vline.png") 0; + &:adjoins-item + { + border-image: url("@{image_url}/tree_branch-more.png") 0; + } + } + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open.png"); + } + } + } +} + +DvItemViewerPanel { + qproperty-TextColor: @m_baseTxtColor; + qproperty-AlternateBackground: #3d3d3d; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: rgb(150, 230, 230); + qproperty-SelectedItemBackground: #80a0dc; +} + +DvDirTreeView { + qproperty-TextColor: @m_baseTxtColor; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: rgb(150, 230, 230); + qproperty-SelectedFolderTextColor: rgb(0,30,0); + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: lighten(@m_baseBG, 5%); +} + +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame{ + border: 1px solid @m_baseTxtColor; +} + +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: @m_titleTxtColor; +} + +#PsdSettingsGroupBox { + border: 1px solid @m_selectedBG; +} + +#FileDoesNotExistLabel { + color: rgb(255,50,50); +} + +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked { + image: url("@{image_url}/minus.png"); + } +} + +ParamsPage { + qproperty-TextColor: @m_baseTxtColor; +} + +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea +{ + border:0px; +} + +#FunctionSegmentViewer +{ + .base_inset; + border-width: 2px; +} + +#xsheetArea, #ScrollArea +{ + .base_inset(10%); + border-width: 2px; +} + +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: rgb(230,230,120); +} +#xsheetColumnAreaMenu_Lock { + background-color: rgb(245,245,245); +} +#xsheetColumnAreaMenu_Camstand { + background-color: rgb(255,164,128); +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; + &:selected { + background-color: rgb(0,0,128); + } +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + .baseBG(light, 10%); + border: 1px solid black; + + &:vertical { + width: 18px; + .set_margin( 0px, 20px ); + } + &:horizontal { + height: 18px; + .set_margin( 20px, 0px ); + } + + &::handle { + border-width: 4; + image-position: center center; + &:vertical { + border-image: url("@{image_url}/sb_g_vhandle.png")4; + image: url("@{image_url}/sb_g_vline.png"); + min-height: 40px; + } + + &:horizontal { + border-image: url("@{image_url}/sb_g_hhandle.png")4; + image: url("@{image_url}/sb_g_hline.png"); + min-width: 40px; + } + } + /* buttons */ + &::add-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; + &:pressed { + image: url("@{image_url}/sb_g_downarrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + &:pressed{ + image: url("@{image_url}/sb_g_rarrow_pressed.png"); + } + } + } + + &::sub-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; + &:pressed { + image: url("@{image_url}/sb_g_uparrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + &:pressed{ + image: url("@{image_url}/sb_g_larrow_pressed.png"); + } + } + } + + &::add-page { + background: none; + } +} + +XsheetViewer { + qproperty-TextColor: rgb(230,230,230); + qproperty-BGColor: rgb(72,72,72); + qproperty-LightLineColor: rgb(32,32,32); + qproperty-MarkerLineColor: rgb(30, 150, 196); + qproperty-PreviewFrameTextColor: rgb(150, 230, 230); + qproperty-CurrentRowBgColor: rgb(80,96,130); + + qproperty-EmptyColumnHeadColor: rgb(96,96,96); + qproperty-SelectedColumnTextColor: rgb(230, 100, 100); + + qproperty-EmptyCellColor: rgb(64,64,64); + qproperty-NotEmptyColumnColor: rgb(72,72,72); + qproperty-SelectedEmptyCellColor: rgb(108,108,108); + + qproperty-LevelColumnColor: rgb(76,110,76); + qproperty-LevelColumnBorderColor: rgb(143,179,143); + qproperty-SelectedLevelColumnColor: rgb(107,140,107); + + qproperty-VectorColumnColor: rgb(123,123,76); + qproperty-VectorColumnBorderColor: rgb(187,187,154); + qproperty-SelectedVectorColumnColor: rgb(140,140,96); + + qproperty-ChildColumnColor: rgb(106,82,107); + qproperty-ChildColumnBorderColor: rgb(177,163,179); + qproperty-SelectedChildColumnColor: rgb(122,97,122); + + qproperty-FullcolorColumnColor: rgb(101,122,150); + qproperty-FullcolorColumnBorderColor: rgb(158,184,187); + qproperty-SelectedFullcolorColumnColor: rgb(136,150,167); + + qproperty-FxColumnColor: rgb(86,85,60); + qproperty-FxColumnBorderColor: rgb(149,149,138); + qproperty-SelectedFxColumnColor: rgb(106,109,90); + + qproperty-ReferenceColumnColor: rgb(97,97,97); + qproperty-ReferenceColumnBorderColor: rgb(162,162,162); + qproperty-SelectedReferenceColumnColor: rgb(130,130,130); + + qproperty-PaletteColumnColor: rgb(58,101,95); + qproperty-PaletteColumnBorderColor: rgb(134,172,167); + qproperty-SelectedPaletteColumnColor: rgb(95,133,129); + + qproperty-ColumnHeadPastelizer: rgb(0,0,0); + qproperty-SelectedColumnHead: rgb(80,96,130); + + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-DarkLineColor: rgb(150,150,150); +} + +/*------- Schematic ---------*/ +#SchematicBottomFrame +{ + margin: 0px; + padding: 0px; + .base_outset; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer +{ + background-color: rgb(55,55,55); +} + +/*------ Function Editor ---------*/ + +#FunctionParametersPanel +{ + border: 1px solid @m_baseTxtColor; +} +#FunctionEditorTree,#ShortcutTree +{ + &::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree17_vline.png") 0; + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-more.png") 0; + } + } + + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open.png"); + } + } + } + } +} + +FunctionPanel { + qproperty-BGColor: rgb(48,48,48); + qproperty-ValueLineColor: rgb(72,72,72); + qproperty-FrameLineColor: rgb(96,96,96); + qproperty-OtherCurvesColor: rgb(128,128,128); + qproperty-RulerBackground: rgb(48,48,48); + qproperty-TextColor: rgb(230,230,230); + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} + +FunctionTreeView { + qproperty-TextColor: rgb(230,230,230); + qproperty-CurrentTextColor: rgb(230, 100, 100); +} + +SpreadsheetViewer { + qproperty-LightLightBGColor: rgb(64,64,64); + qproperty-CurrentRowBgColor: rgb(80,96,130); + qproperty-LightLineColor: rgb(32,32,32); + qproperty-MarkerLineColor: rgb(30, 150, 196); + qproperty-BGColor: rgb(72,72,72); + qproperty-VerticalLineColor: rgb(120,120,120); + qproperty-KeyFrameColor: rgb(153,93,29); + qproperty-KeyFrameBorderColor: rgb(201,176,75); + qproperty-SelectedKeyFrameColor: rgb(151,128,86); + qproperty-InBetweenColor: rgb(102,98,80); + qproperty-InBetweenBorderColor: rgb(205,206,200); + qproperty-SelectedInBetweenColor: rgb(126,128,121); + qproperty-SelectedEmptyColor: rgb(108,108,108); + qproperty-SelectedSceneRangeEmptyColor: rgb(117,117,117); + qproperty-TextColor: rgb(230,230,230); + qproperty-ColumnHeaderBorderColor: rgb(142,142,142); + qproperty-SelectedColumnTextColor: rgb(230, 100, 100); +} +#keyFrameNavigator +{ + border: 0px; + margin: 0px; + padding: 0px; +} + +#ExpressionField +{ + .base_inset(light, 50%); + border-width: 2px; + border-radius: 2px; + margin: 0px; +} + +#FunctionSegmentViewerLinkButton +{ + border: 2px; + margin: 0px; + background-image: url("@{image_url}/segment_unlinked.png"); + .base_outset(light,20%); + &:checked { + background-image: url("@{image_url}/segment_linked.png"); + .base_inset(light,20%); + } + &:disabled{ + background-image: url("@{image_url}/segment_disabled.png"); + .base_outset(light,10%); + border: 1px; + } +} + +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + .base_inset(10%); + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel +{ + color: @m_titleTxtColor; +} + +/*------ Cleanup Settings------*/ + +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} + +#CameraSettingsButton +{ + padding: 2px; + border: 0px; +} + +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } +} + +#CameraSettingsDPI{ + color: @m_titleTxtColor; +} + +#CameraSettingsRadioButton_Small { + padding: 2px; + &::indicator { + width: 11px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock_small.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock_small.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover_small.png"); + } + } + } +} + +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("@{image_url}/fsp_released.png"); + + &:hover { + image: url("@{image_url}/fsp_hover.png"); + } + &:checked { + image: url("@{image_url}/fsp_pressed.png"); + } +} + +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; /*space between button and text*/ + &::indicator { + border-width: 0px; + width: 21px; + height: 21px; + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_unlock_hover.png"); + } + } + &:checked { + image: url("@{image_url}/cam_lock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } + } +} + +/*------ Topbar and Menubar of the MainWindow ------*/ + +#TopBar { + height: 22px; + .baseBG(5%); + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + .baseBG; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("@{image_url}/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; + &::tab { + .set_margin(5px, 1px); + .set_padding(8px, 1px); + .baseBG( light, 5% ); + border: 1px solid white; + &:selected { + background-color: rgb(90,140,120); + } + &:hover { + background-color: rgb(120,120,90); + } + } +} +#StackedMenuBar +{ + background: rgb(160,160,160); + margin: 0px; + border: 0px; + padding: 0px; +} + +#DockSeparator{ + .base_outset(light, 10%); + //border-image: url("@{image_url}/dock_handle_border.png") 2; + border-width: 1; +} + +#TDockPlaceholder { + background-color: rgb(185,240,0,255); +} + +/*------ Popups -------*/ + +QDialog #dialogButtonFrame { + .baseBG(10%); +} + +#SceneSettings QLabel +{ + color: @m_titleTxtColor; +} +#PreferencesPopup QListWidget +{ + .base_inset; + border-width: 2px; + alternate-background-color: lighten(@m_baseBG, 5%); + font-size: 14px; + &::item{ + padding: 3px; + &:selected{ + background-color: @m_selectedBG; + color : black; + } + &:hover{ + background-color: lighten(@m_baseBG, 10%); + } + } +} +#OutputSettingsBox { + border:1px solid @m_selectedBG; +} + +#OutputSettingsLabel { + color: @m_titleTxtColor; +} + +#OutputSettingsCameraBox { + .base_inset; + border-width: 2px; +} + +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked + { + image: url("@{image_url}/minus.png"); + } +} + +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: rgb(155,155,155); + qproperty-DarkLineColor: rgb(47,47,47); + qproperty-HandleLeftPixmap: url("@{image_url}/h_slider_left.png"); + qproperty-HandleRightPixmap: url("@{image_url}/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("@{image_url}/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("@{image_url}/h_slider_right_disabled.png"); +} + +#FxSettingsLabel{ + color: rgb(160,230,128); +} + +#FxSettings{ + border-width: 0px; + border-bottom: 3px double rgb(64,64,64); +} + +#FxSettingsHelpButton{ + color: rgb(160,200,255); +} + +#MatchLineButton { + .baseBG(light, 10%); + &::pressed + { + .baseBG(light, 30%); + } +} \ No newline at end of file diff --git a/stuff/config/qss/gray_048/gray_048.qss b/stuff/config/qss/gray_048/gray_048.qss new file mode 100644 index 0000000..7acd983 --- /dev/null +++ b/stuff/config/qss/gray_048/gray_048.qss @@ -0,0 +1,1298 @@ +/* LESS Definitions */ +/*Image URL*/ +/*Text Color*/ +/*Used in Dialog border*/ +/*Color for Selected Item*/ +/*Color for title texts*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QWidget { + color: #e6e6e6; + background-color: #303030; +} +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #303030; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + alternate-background-color: #3d3d3d; +} +QTreeWidget::item:selected { + background-color: #80a0dc; + color: black; +} +QTreeWidget::item { + color: #e6e6e6; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background-color: #232323; +} +QMenuBar::item:selected { + background-color: #232323; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #4b4b4b; + border-bottom-color: #5f5f5f; + border-width: 1px; +} +QMenu { + background-color: #232323; +} +QMenu::item:selected { + background: #80a0dc; + color: black; +} +QMenu::item:disabled { + background-color: #3d3d3d; + color: #808080; +} +QMenu::item:disabled:selected { + background: #6c7680; +} +QMenu::separator { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + margin-left: 5px; + margin-right: 5px; + margin-top: 2px; + margin-bottom: 2px; + border-width: 1px; + height: 0px; +} +QToolBar { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + image: url("qss/gray_072/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_072/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #303030; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_072/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_072/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + background-color: #3d3d3d; + color: #808080; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ +} +QToolBar QLabel { + background-color: #303030; + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QLineEdit { + /*darken little bit*/ + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + border-radius: 2px; +} +QLineEdit:disabled { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; + color: #808080; +} +QComboBox { + /*darken little bit*/ + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + /*arrow button*/ + /*arrow button triangle*/ +} +QComboBox::drop-down { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 2px; + /*pressed state*/ +} +QComboBox::drop-down:on { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; +} +QComboBox::down-arrow { + image: url("qss/gray_072/imgs/combo_down_arrow.png"); +} +QComboBox:disabled { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; + color: #808080; +} +QPushButton { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + border-radius: 4px; + padding-left: 20px; + padding-right: 20px; + padding-top: 3px; + padding-bottom: 3px; + /*lighten lilttle bit when hover*/ + /*lighten lilttle bit when pressed*/ +} +QPushButton:hover { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; +} +QPushButton:hover:pressed { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; +} +QPushButton:checked { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; +} +QPushButton:disabled { + background-color: #3d3d3d; + border-style: outset; + border-left-color: #656565; + border-top-color: #797979; + border-right-color: #191919; + border-bottom-color: #0d0d0d; + color: #505050; +} +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +QCheckBox:hover { + background-color: #4a4a4a; +} +QCheckBox:disabled { + color: #808080; +} +QCheckBox::indicator { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 2px; +} +QCheckBox::indicator:disabled { + background-color: #3d3d3d; + border-style: inset; + border-left-color: #191919; + border-top-color: #0d0d0d; + border-right-color: #656565; + border-bottom-color: #797979; +} +QCheckBox::indicator:checked { + image: url("qss/gray_072/imgs/check_indicator.png"); +} +QCheckBox::indicator:checked:disabled { + image: url("qss/gray_072/imgs/check_indicator_disabled.png"); +} +QSlider::groove:horizontal { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + height: 1px; + margin: 1px; +} +QSlider::handle:horizontal { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border-width: 2px; + width: 5px; + margin: -8px 0px; + /* expand outside the groove */ +} +QGroupBox { + border: 1px solid #e6e6e6; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} +QSplitter::handle { + background-color: #707070; +} +/* ------ Toonz Classes Difinitions ------ */ +TPanel { + /*Used for dialog border*/ + background-color: #000000; +} +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #0a0a0a; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #0a0a0a; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #232323; + border-style: outset; + border-left-color: #4b4b4b; + border-top-color: #5f5f5f; + border-right-color: #000000; + border-bottom-color: #000000; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #6c6c6c; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + margin-top: 0px; + border-bottom-color: #303030; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_072/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_072/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_072/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: #e6e6e6; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #0a0a0a; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #232323; + border-style: outset; + border-left-color: #4b4b4b; + border-top-color: #5f5f5f; + border-right-color: #000000; + border-bottom-color: #000000; + /* for non selected tab */ + border-bottom-color: #6c6c6c; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-bottom-color: #303030; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #303030; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_072/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_072/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_072/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_072/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_072/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-top-width: 1px; + border-bottom-width: 1px; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_072/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_072/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_072/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_072/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #0a0a0a; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #6c6c6c; + margin-top: 1px; + padding-top: 3px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #6c6c6c; + margin-top: 1px; + padding-top: 3px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #4a4a4a; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_072/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_072/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_072/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_072/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_072/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-baseColor: #626262; +} +Ruler { + qproperty-ParentBGColor: #303030; + qproperty-ScaleColor: #e6e6e6; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; +} +#RulerToolOptionValues { + color: black; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #3d3d3d; + border-width: 1px; + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: #e6e6e6; + qproperty-AlternateBackground: #3d3d3d; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedItemBackground: #80a0dc; +} +DvDirTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedFolderTextColor: #001e00; + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: #3d3d3d; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #e6e6e6; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #a8bee7; +} +#PsdSettingsGroupBox { + border: 1px solid #80a0dc; +} +#FileDoesNotExistLabel { + color: #ff3232; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: #e6e6e6; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#FunctionSegmentViewer { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; +} +#xsheetArea, +#ScrollArea { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 2px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #4a4a4a; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_072/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_072/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_072/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_072/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: #e6e6e6; + qproperty-BGColor: #484848; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-PreviewFrameTextColor: #96e6e6; + qproperty-CurrentRowBgColor: #506082; + qproperty-EmptyColumnHeadColor: #606060; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #404040; + qproperty-NotEmptyColumnColor: #484848; + qproperty-SelectedEmptyCellColor: #6c6c6c; + qproperty-LevelColumnColor: #4c6e4c; + qproperty-LevelColumnBorderColor: #8fb38f; + qproperty-SelectedLevelColumnColor: #6b8c6b; + qproperty-VectorColumnColor: #7b7b4c; + qproperty-VectorColumnBorderColor: #bbbb9a; + qproperty-SelectedVectorColumnColor: #8c8c60; + qproperty-ChildColumnColor: #6a526b; + qproperty-ChildColumnBorderColor: #b1a3b3; + qproperty-SelectedChildColumnColor: #7a617a; + qproperty-FullcolorColumnColor: #657a96; + qproperty-FullcolorColumnBorderColor: #9eb8bb; + qproperty-SelectedFullcolorColumnColor: #8896a7; + qproperty-FxColumnColor: #56553c; + qproperty-FxColumnBorderColor: #95958a; + qproperty-SelectedFxColumnColor: #6a6d5a; + qproperty-ReferenceColumnColor: #616161; + qproperty-ReferenceColumnBorderColor: #a2a2a2; + qproperty-SelectedReferenceColumnColor: #828282; + qproperty-PaletteColumnColor: #3a655f; + qproperty-PaletteColumnBorderColor: #86aca7; + qproperty-SelectedPaletteColumnColor: #5f8581; + qproperty-ColumnHeadPastelizer: #000000; + qproperty-SelectedColumnHead: #506082; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #e6e6e6; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #303030; + qproperty-ValueLineColor: #484848; + qproperty-FrameLineColor: #606060; + qproperty-OtherCurvesColor: #808080; + qproperty-RulerBackground: #303030; + qproperty-TextColor: #e6e6e6; + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} +FunctionTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-CurrentTextColor: #e66464; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #404040; + qproperty-CurrentRowBgColor: #506082; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-BGColor: #484848; + qproperty-VerticalLineColor: #787878; + qproperty-KeyFrameColor: #995d1d; + qproperty-KeyFrameBorderColor: #c9b04b; + qproperty-SelectedKeyFrameColor: #978056; + qproperty-InBetweenColor: #666250; + qproperty-InBetweenBorderColor: #cdcec8; + qproperty-SelectedInBetweenColor: #7e8079; + qproperty-SelectedEmptyColor: #6c6c6c; + qproperty-SelectedSceneRangeEmptyColor: #757575; + qproperty-TextColor: #e6e6e6; + qproperty-ColumnHeaderBorderColor: #8e8e8e; + qproperty-SelectedColumnTextColor: #e66464; +} +#keyFrameNavigator { + border: 0px; + margin: 0px; + padding: 0px; +} +#ExpressionField { + background-color: #b0b0b0; + border-style: inset; + border-left-color: #8c8c8c; + border-top-color: #808080; + border-right-color: #d8d8d8; + border-bottom-color: #ececec; + border-width: 2px; + border-radius: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + background-image: url("qss/gray_072/imgs/segment_unlinked.png"); + background-color: #636363; + border-style: outset; + border-left-color: #8b8b8b; + border-top-color: #9f9f9f; + border-right-color: #3f3f3f; + border-bottom-color: #333333; +} +#FunctionSegmentViewerLinkButton:checked { + background-image: url("qss/gray_072/imgs/segment_linked.png"); + background-color: #636363; + border-style: inset; + border-left-color: #3f3f3f; + border-top-color: #333333; + border-right-color: #8b8b8b; + border-bottom-color: #9f9f9f; +} +#FunctionSegmentViewerLinkButton:disabled { + background-image: url("qss/gray_072/imgs/segment_disabled.png"); + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #a8bee7; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #a8bee7; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_072/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_072/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_072/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #232323; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background-color: #303030; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_072/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #3d3d3d; + border: 1px solid white; +} +#TopBarTab::tab:selected { + background-color: #5a8c78; +} +#TopBarTab::tab:hover { + background-color: #78785a; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border-width: 1; +} +#TDockPlaceholder { + background-color: #b9f000; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #161616; +} +#SceneSettings QLabel { + color: #a8bee7; +} +#PreferencesPopup QListWidget { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; + alternate-background-color: #3d3d3d; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #80a0dc; + color: black; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #4a4a4a; +} +#OutputSettingsBox { + border: 1px solid #80a0dc; +} +#OutputSettingsLabel { + color: #a8bee7; +} +#OutputSettingsCameraBox { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: #9b9b9b; + qproperty-DarkLineColor: #2f2f2f; + qproperty-HandleLeftPixmap: url("qss/gray_072/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_072/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_072/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_072/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #a0e680; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + color: #a0c8ff; +} +#MatchLineButton { + background-color: #4a4a4a; +} +#MatchLineButton::pressed { + background-color: #7d7d7d; +} + +//# sourceMappingURL=gray_048.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_048/gray_048_mac.qss b/stuff/config/qss/gray_048/gray_048_mac.qss new file mode 100644 index 0000000..31f4a62 --- /dev/null +++ b/stuff/config/qss/gray_048/gray_048_mac.qss @@ -0,0 +1,1306 @@ +/* LESS Definitions */ +/*Image URL*/ +/*Text Color*/ +/*Used in Dialog border*/ +/*Color for Selected Item*/ +/*Color for title texts*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QWidget { + color: #e6e6e6; + background-color: #303030; +} +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #303030; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + alternate-background-color: #3d3d3d; +} +QTreeWidget::item:selected { + background-color: #80a0dc; + color: black; +} +QTreeWidget::item { + color: #e6e6e6; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background-color: #232323; +} +QMenuBar::item:selected { + background-color: #232323; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #4b4b4b; + border-bottom-color: #5f5f5f; + border-width: 1px; +} +QMenu { + background-color: #232323; +} +QMenu::item:selected { + background: #80a0dc; + color: black; +} +QMenu::item:disabled { + background-color: #3d3d3d; + color: #808080; +} +QMenu::item:disabled:selected { + background: #6c7680; +} +QMenu::separator { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + margin-left: 5px; + margin-right: 5px; + margin-top: 2px; + margin-bottom: 2px; + border-width: 1px; + height: 0px; +} +QToolBar { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + image: url("qss/gray_072/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_072/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #303030; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_072/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_072/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + background-color: #3d3d3d; + color: #808080; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ +} +QToolBar QLabel { + background-color: #303030; + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QLineEdit { + /*darken little bit*/ + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + border-radius: 2px; +} +QLineEdit:disabled { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; + color: #808080; +} +QComboBox { + /*darken little bit*/ + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + /*arrow button*/ + /*arrow button triangle*/ +} +QComboBox::drop-down { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 2px; + /*pressed state*/ +} +QComboBox::drop-down:on { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; +} +QComboBox::down-arrow { + image: url("qss/gray_072/imgs/combo_down_arrow.png"); +} +QComboBox:disabled { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; + color: #808080; +} +QPushButton { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + border-radius: 4px; + padding-left: 20px; + padding-right: 20px; + padding-top: 3px; + padding-bottom: 3px; + /*lighten lilttle bit when hover*/ + /*lighten lilttle bit when pressed*/ +} +QPushButton:hover { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; +} +QPushButton:hover:pressed { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; +} +QPushButton:pressed { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; +} +QPushButton:checked { + background-color: #4a4a4a; + border-style: inset; + border-left-color: #262626; + border-top-color: #1a1a1a; + border-right-color: #727272; + border-bottom-color: #868686; +} +QPushButton:disabled { + background-color: #3d3d3d; + border-style: outset; + border-left-color: #656565; + border-top-color: #797979; + border-right-color: #191919; + border-bottom-color: #0d0d0d; + color: #505050; +} +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +QCheckBox:hover { + background-color: #4a4a4a; +} +QCheckBox:disabled { + color: #808080; +} +QCheckBox::indicator { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 2px; +} +QCheckBox::indicator:disabled { + background-color: #3d3d3d; + border-style: inset; + border-left-color: #191919; + border-top-color: #0d0d0d; + border-right-color: #656565; + border-bottom-color: #797979; +} +QCheckBox::indicator:checked { + image: url("qss/gray_072/imgs/check_indicator.png"); +} +QCheckBox::indicator:checked:disabled { + image: url("qss/gray_072/imgs/check_indicator_disabled.png"); +} +QSlider::groove:horizontal { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + height: 1px; + margin: 1px; +} +QSlider::handle:horizontal { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border-width: 2px; + width: 5px; + margin: -8px 0px; + /* expand outside the groove */ +} +QGroupBox { + border: 1px solid #e6e6e6; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} +QSplitter::handle { + background-color: #707070; +} +/* ------ Toonz Classes Difinitions ------ */ +TPanel { + /*Used for dialog border*/ + background-color: #000000; +} +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #0a0a0a; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #0a0a0a; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #232323; + border-style: outset; + border-left-color: #4b4b4b; + border-top-color: #5f5f5f; + border-right-color: #000000; + border-bottom-color: #000000; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #6c6c6c; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + margin-top: 0px; + border-bottom-color: #303030; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_072/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_072/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_072/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: #e6e6e6; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #0a0a0a; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #232323; + border-style: outset; + border-left-color: #4b4b4b; + border-top-color: #5f5f5f; + border-right-color: #000000; + border-bottom-color: #000000; + /* for non selected tab */ + border-bottom-color: #6c6c6c; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-bottom-color: #303030; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #303030; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_072/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_072/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_072/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_072/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_072/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-top-width: 1px; + border-bottom-width: 1px; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_072/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_072/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_072/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_072/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #0a0a0a; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #6c6c6c; + margin-top: 1px; + padding-top: 3px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #6c6c6c; + margin-top: 1px; + padding-top: 3px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #4a4a4a; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_072/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_072/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_072/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_072/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_072/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-baseColor: #626262; +} +Ruler { + qproperty-ParentBGColor: #303030; + qproperty-ScaleColor: #e6e6e6; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; +} +#RulerToolOptionValues { + color: black; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #3d3d3d; + border-width: 1px; + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: #e6e6e6; + qproperty-AlternateBackground: #3d3d3d; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedItemBackground: #80a0dc; +} +DvDirTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedFolderTextColor: #001e00; + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: #3d3d3d; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #e6e6e6; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #a8bee7; +} +#PsdSettingsGroupBox { + border: 1px solid #80a0dc; +} +#FileDoesNotExistLabel { + color: #ff3232; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: #e6e6e6; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#FunctionSegmentViewer { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; +} +#xsheetArea, +#ScrollArea { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 2px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #4a4a4a; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_072/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_072/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_072/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_072/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: #e6e6e6; + qproperty-BGColor: #484848; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-PreviewFrameTextColor: #96e6e6; + qproperty-CurrentRowBgColor: #506082; + qproperty-EmptyColumnHeadColor: #606060; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #404040; + qproperty-NotEmptyColumnColor: #484848; + qproperty-SelectedEmptyCellColor: #6c6c6c; + qproperty-LevelColumnColor: #4c6e4c; + qproperty-LevelColumnBorderColor: #8fb38f; + qproperty-SelectedLevelColumnColor: #6b8c6b; + qproperty-VectorColumnColor: #7b7b4c; + qproperty-VectorColumnBorderColor: #bbbb9a; + qproperty-SelectedVectorColumnColor: #8c8c60; + qproperty-ChildColumnColor: #6a526b; + qproperty-ChildColumnBorderColor: #b1a3b3; + qproperty-SelectedChildColumnColor: #7a617a; + qproperty-FullcolorColumnColor: #657a96; + qproperty-FullcolorColumnBorderColor: #9eb8bb; + qproperty-SelectedFullcolorColumnColor: #8896a7; + qproperty-FxColumnColor: #56553c; + qproperty-FxColumnBorderColor: #95958a; + qproperty-SelectedFxColumnColor: #6a6d5a; + qproperty-ReferenceColumnColor: #616161; + qproperty-ReferenceColumnBorderColor: #a2a2a2; + qproperty-SelectedReferenceColumnColor: #828282; + qproperty-PaletteColumnColor: #3a655f; + qproperty-PaletteColumnBorderColor: #86aca7; + qproperty-SelectedPaletteColumnColor: #5f8581; + qproperty-ColumnHeadPastelizer: #000000; + qproperty-SelectedColumnHead: #506082; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #303030; + border-style: outset; + border-left-color: #585858; + border-top-color: #6c6c6c; + border-right-color: #0c0c0c; + border-bottom-color: #000000; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #e6e6e6; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #303030; + qproperty-ValueLineColor: #484848; + qproperty-FrameLineColor: #606060; + qproperty-OtherCurvesColor: #808080; + qproperty-RulerBackground: #303030; + qproperty-TextColor: #e6e6e6; + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} +FunctionTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-CurrentTextColor: #e66464; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #404040; + qproperty-CurrentRowBgColor: #506082; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-BGColor: #484848; + qproperty-VerticalLineColor: #787878; + qproperty-KeyFrameColor: #995d1d; + qproperty-KeyFrameBorderColor: #c9b04b; + qproperty-SelectedKeyFrameColor: #978056; + qproperty-InBetweenColor: #666250; + qproperty-InBetweenBorderColor: #cdcec8; + qproperty-SelectedInBetweenColor: #7e8079; + qproperty-SelectedEmptyColor: #6c6c6c; + qproperty-SelectedSceneRangeEmptyColor: #757575; + qproperty-TextColor: #e6e6e6; + qproperty-ColumnHeaderBorderColor: #8e8e8e; + qproperty-SelectedColumnTextColor: #e66464; +} +#keyFrameNavigator { + border: 0px; + margin: 0px; + padding: 0px; +} +#ExpressionField { + background-color: #b0b0b0; + border-style: inset; + border-left-color: #8c8c8c; + border-top-color: #808080; + border-right-color: #d8d8d8; + border-bottom-color: #ececec; + border-width: 2px; + border-radius: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + background-image: url("qss/gray_072/imgs/segment_unlinked.png"); + background-color: #636363; + border-style: outset; + border-left-color: #8b8b8b; + border-top-color: #9f9f9f; + border-right-color: #3f3f3f; + border-bottom-color: #333333; +} +#FunctionSegmentViewerLinkButton:checked { + background-image: url("qss/gray_072/imgs/segment_linked.png"); + background-color: #636363; + border-style: inset; + border-left-color: #3f3f3f; + border-top-color: #333333; + border-right-color: #8b8b8b; + border-bottom-color: #9f9f9f; +} +#FunctionSegmentViewerLinkButton:disabled { + background-image: url("qss/gray_072/imgs/segment_disabled.png"); + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #161616; + border-style: inset; + border-left-color: #000000; + border-top-color: #000000; + border-right-color: #3f3f3f; + border-bottom-color: #525252; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #a8bee7; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #a8bee7; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_072/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_072/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_072/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #232323; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background-color: #303030; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_072/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #3d3d3d; + border: 1px solid white; +} +#TopBarTab::tab:selected { + background-color: #5a8c78; +} +#TopBarTab::tab:hover { + background-color: #78785a; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #4a4a4a; + border-style: outset; + border-left-color: #727272; + border-top-color: #868686; + border-right-color: #262626; + border-bottom-color: #1a1a1a; + border-width: 1; +} +#TDockPlaceholder { + background-color: #b9f000; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #161616; +} +#SceneSettings QLabel { + color: #a8bee7; +} +#PreferencesPopup QListWidget { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; + alternate-background-color: #3d3d3d; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #80a0dc; + color: black; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #4a4a4a; +} +#OutputSettingsBox { + border: 1px solid #80a0dc; +} +#OutputSettingsLabel { + color: #a8bee7; +} +#OutputSettingsCameraBox { + background-color: #303030; + border-style: inset; + border-left-color: #0c0c0c; + border-top-color: #000000; + border-right-color: #585858; + border-bottom-color: #6c6c6c; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: #9b9b9b; + qproperty-DarkLineColor: #2f2f2f; + qproperty-HandleLeftPixmap: url("qss/gray_072/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_072/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_072/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_072/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #a0e680; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + color: #a0c8ff; +} +#MatchLineButton { + background-color: #4a4a4a; +} +#MatchLineButton::pressed { + background-color: #7d7d7d; +} + +//# sourceMappingURL=gray_048.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072.less b/stuff/config/qss/gray_072/gray_072.less new file mode 100644 index 0000000..8cc26a3 --- /dev/null +++ b/stuff/config/qss/gray_072/gray_072.less @@ -0,0 +1,1298 @@ +/* LESS Definitions */ + +/*Image URL*/ +@base_url: "qss/gray_072"; +@image_url: "@{base_url}/imgs"; + +/*Text Color*/ +@m_baseTxtColor: rgb(230,230,230); +@m_disabledTxtColor: rgb(128,128,128); + +@m_baseBG: rgb(72,72,72); +@m_base_lightH: rgb(108,108,108); +@m_base_lightV: rgb(128,128,128); +@m_base_darkH: rgb(32,32,32); +@m_base_darkV: rgb(0,0,0); + +/*Used in Dialog border*/ +@m_dialog_border_color: rgb(32,32,32); + +/*Color for Selected Item*/ +@m_selectedBG: rgb(128,160,220); + +/*Color for title texts*/ +@m_titleTxtColor: lighten(@m_selectedBG, 10%); + +/* color adjustable by delta */ +.baseBG(@dark: 0%){ + background-color: darken(@m_baseBG, @dark); +} +.baseBG(light, @light: 0%){ + background-color: lighten(@m_baseBG, @light); +} + +.base_inset(@dark: 0%){ + .baseBG(@dark); + border-style: inset; + border-left-color: darken(@m_base_darkH, @dark); + border-top-color: darken(@m_base_darkV, @dark); + border-right-color: darken(@m_base_lightH, @dark); + border-bottom-color: darken(@m_base_lightV, @dark); +} +.base_inset(light, @light: 0%){ + .baseBG(light, @light); + border-style: inset; + border-left-color: lighten(@m_base_darkH, @light); + border-top-color: lighten(@m_base_darkV, @light); + border-right-color: lighten(@m_base_lightH, @light); + border-bottom-color: lighten(@m_base_lightV, @light); +} + +.base_outset(@dark: 0%){ + .baseBG(@dark); + border-style: outset; + border-left-color: darken(@m_base_lightH, @dark); + border-top-color: darken(@m_base_lightV, @dark); + border-right-color: darken(@m_base_darkH, @dark); + border-bottom-color: darken(@m_base_darkV, @dark); +} +.base_outset(light, @light: 0%){ + .baseBG(light, @light); + border-style: outset; + border-left-color: lighten(@m_base_lightH, @light); + border-top-color: lighten(@m_base_lightV, @light); + border-right-color: lighten(@m_base_darkH, @light); + border-bottom-color: lighten(@m_base_darkV, @light); +} + +/*set padding*/ +.set_padding(@hPad: 0px, @vPad: 0px){ + padding-left: @hPad; + padding-right: @hPad; + padding-top: @vPad; + padding-bottom: @vPad; +} +/*set margin*/ +.set_margin(@hMgn: 0px, @vMgn: 0px) { + margin-left: @hMgn; + margin-right: @hMgn; + margin-top: @vMgn; + margin-bottom: @vMgn; +} + +/* ------ Qt Widgets Common Difinitions ------ */ + +QWidget { + color: @m_baseTxtColor; + .baseBG; +} + +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} + +QDialog +{ + .baseBG; +} + +QMainWindow::separator +{ + background: yellow; + width: 10px; /* when vertical */ + height: 10px; /* when horizontal */ +} + +QToolTip, #helpTooltip +{ + border: 1px solid black; + background-color: rgb(255,255,225); + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + .base_inset; + alternate-background-color: lighten(@m_baseBG, 5%); + + &::item:selected + { + background-color: @m_selectedBG; + color: black; + } + &::item + { + color: @m_baseTxtColor; + } + +} +QStatusBar { + background-color: rgb(192,192,192); + + &::item { + border-width: 0; + } + & QLabel { + background-color: rgb(192,192,192); + } + & #StatusBarLabel { + background-color: rgb(255,255,255); + .set_padding( 3px, 1px ); + } +} +QMenuBar +{ + .baseBG(5%); + &::item:selected{ + .base_inset(5%); + border-width: 1px; + } +} + +QMenu +{ + .baseBG(5%); + + &::item { + &:selected{ + background: @m_selectedBG; + color: black; + } + &:disabled{ + .baseBG(light, 5%); + color: @m_disabledTxtColor; + } + &:disabled:selected{ + background: rgb(108,118,128); + } + } + + &::separator { + .base_inset(10%); + .set_margin(5px,2px); + border-width: 1px; + height: 0px; + } +} + +QToolBar +{ + .base_outset; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; + + &::separator:horizontal { + image: url("@{image_url}/bottomseparator.png"); + } + &::separator:vertical { + image: url("@{image_url}/separator.png"); + } + + & QToolButton { + .baseBG; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; + &:hover { + border-image: url("@{image_url}/over.png") 2; + } + &:checked, + &:pressed { + border-image: url("@{image_url}/click.png") 2; + } + &:disabled{ + .baseBG(light, 5%); + color: @m_disabledTxtColor; + } + &::menu-indicator + { + image: none; + } + &::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ + } + } + + & QLabel + { + .baseBG; + margin-top: 1px; + border-width: 2; + } + + & QToolBar + { + border-width: 0px; + } +} + +QLineEdit { + /*darken little bit*/ + .base_inset(10%); + border-width: 1px; + border-radius: 2px; + &:disabled { + .base_inset(light, 10%); + color: @m_disabledTxtColor; + } +} +QComboBox { + /*darken little bit*/ + .base_inset(10%); + border-width: 1px; + .set_padding( 3px, 0px ); + + /*arrow button*/ + &::drop-down { + .base_outset; + border-width: 2px; + /*pressed state*/ + &:on { + .base_inset; + } + } + /*arrow button triangle*/ + &::down-arrow { + image: url("@{image_url}/combo_down_arrow.png"); + } + &:disabled { + .base_inset(light, 10%); + color: @m_disabledTxtColor; + } + +} + +QPushButton { + .base_outset; + border-width: 1px; + border-radius: 4px; + .set_padding(20px, 3px); + + /*lighten lilttle bit when hover*/ + &:hover { + .base_outset(light, 10%); + &:pressed { + .base_inset(light, 10%); + } + } + /*lighten lilttle bit when pressed*/ + &:checked { + .base_inset(light, 10%); + } + &:disabled{ + .base_outset(light, 5%); + color: rgb(80,80,80); + } +} + +#PushButton_NoPadding { + .set_padding(3px, 3px); +} + +QCheckBox { + &:hover { + .baseBG(light, 10%); + } + &:disabled { + color: rgb(80,80,80); + } + &::indicator { + .base_inset(10%); + border-width: 2px; + &:disabled { + .base_inset(light, 5%); + } + &:checked { + image: url("@{image_url}/check_indicator.png"); + &:disabled { + image: url("@{image_url}/check_indicator_disabled.png"); + } + } + } +} + +QSlider { + &::groove:horizontal { + .base_inset(10%); + border-width: 1px; + height: 1px; + margin: 1px; + } + &::handle:horizontal { + .base_outset(light, 10%); + border-width: 2px; + width: 5px; + margin: -8px 0px; /* expand outside the groove */ + } +} + +QGroupBox { + border: 1px solid @m_baseTxtColor; + .set_margin( 5px, 5px ); + .set_padding( 3px, 5px ); + &::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; + } +} + +QSplitter::handle +{ + background-color: lighten(@m_baseBG, 25); +} +/* ------ Toonz Classes Difinitions ------ */ + +TPanel { + /*Used for dialog border*/ + background-color: @m_dialog_border_color; +} + +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer +{ + margin: 0px; + padding: 0px; + & QToolBar + { + border: 1px; + .base_outset(); + & QToolButton + { + margin: 0px; + padding: 1px; + border: 0px; + } + } + & #keyFrameNavigator + { + border: 0px; + } +} +#TabBarContainer{ + .baseBG(15%); + & #ScrollLeftButton, + & #ScrollRightButton{ + margin-top: 1px; + } +} +#PaletteTabBar, +#FxSettingsTabBar{ + .baseBG(15%); + &::tab { + .set_padding( 7px, 2px ); + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + .base_outset(5%); /* for non selected tab */ + margin-top: 2px; /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset(); + margin-top: 0px; + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; + } + + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#PaletteLockButton{ + &:hover{ + border-image: url("@{image_url}/over_yellow.png") 2; + } + &:checked{ + border-image: url("@{image_url}/click_pink.png") 2; + &:hover{ + border-image: url("@{image_url}/over_pressed_yellow.png") 2; + } + } +} + +#PageViewer{ + qproperty-TextColor: @m_baseTxtColor; +} + +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + .baseBG(15%); + &::tab{ + .set_padding( 2px, 1px ); + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + .base_outset(5%); /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset; + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; + } + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#HexagonalColorWheel { + qproperty-BGColor: @m_baseBG; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider { + &::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; + } + &::handle:horizontal { + width: 8px; + margin: -8px -4px; + } +} + +#colorSliderAddButton, +#colorSliderSubButton +{ + border-image: url("@{image_url}/colorslider_button_bg.png")2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} + +#colorSliderAddButton +{ + image: url("@{image_url}/colorslider_add.png"); + &:pressed { + image: url("@{image_url}/colorslider_add_pressed.png"); + } +} + +#colorSliderSubButton +{ + image: url("@{image_url}/colorslider_sub.png"); + &:pressed { + image: url("@{image_url}/colorslider_sub_pressed.png"); + } +} + +#PlainColorPageParts +{ + .base_outset; + border-top-width: 1px; + border-bottom-width: 1px; +} + +#colorSliderLabel, +#colorSliderField +{ + font-size: 14px; +} + + +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: rgb(225,225,225); + + &:hover { + background-color: rgb(245,245,245); + } + &:pressed { + background-color: rgb(215,215,215); + } +} + +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} + +#ScrollLeftButton { + image: url("@{image_url}/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("@{image_url}/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("@{image_url}/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("@{image_url}/down_arrow_black.png"); + border-top: 1px solid black; +} + +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + .baseBG(15%); + + & #ToolBarContainer + { + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 3px; + } +} + +FlipBook #ToolBarContainer +{ + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 3px; +} + +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} + +#ViewerFpsSlider +{ + .baseBG(light, 10%); + .set_margin(19px, 0px); + border: 1px solid black; + height: 21px; + + &::handle { + border-image: url("@{image_url}/handle_border.png")6; + border-width: 6px; + image: none; + min-width: 5px; + } + &::add-line { + image: url("@{image_url}/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_rarrow_pressed.png"); + } + } + &::sub-line { + image: url("@{image_url}/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_larrow_pressed.png"); + } + } +} + +#FlipConsolePlayToolBar{ + border: none; + & QToolButton { + height: 14px; + } +} + +FlipSlider { + qproperty-PBHeight: 20; + + qproperty-PBOverlay: url("@{image_url}/flipslider.png"); + qproperty-PBMarker: url("@{image_url}/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-baseColor: #626262; + qproperty-notStartedColor: rgb(204,34,34); + qproperty-startedColor: rgb(200,128,128); + qproperty-baseColor: #626262; +} + +Ruler { + qproperty-ParentBGColor: rgb(48,48,48); + qproperty-ScaleColor: rgb(230,230,230); +} + +#ComboViewerToolOptions{ + border: 1px; + .base_outset; +} + +#RulerToolOptionValues{ + color: black; +} + +/*-----------File Browser------------*/ +#DirTreeView, #FunctionEditorTree, #ShortcutTree, #FxTreeView +{ + alternate-background-color: lighten(@m_baseBG, 5%); + border-width: 1px; + .base_inset; + margin: 0px; +} +#DirTreeView::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree_vline.png") 0; + &:adjoins-item + { + border-image: url("@{image_url}/tree_branch-more.png") 0; + } + } + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open.png"); + } + } + } +} + +DvItemViewerPanel { + qproperty-TextColor: @m_baseTxtColor; + qproperty-AlternateBackground: #555555; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: rgb(150, 230, 230); + qproperty-SelectedItemBackground: #80a0dc; +} + +DvDirTreeView { + qproperty-TextColor: @m_baseTxtColor; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: rgb(150, 230, 230); + qproperty-SelectedFolderTextColor: rgb(0,30,0); + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: lighten(@m_baseBG, 5%); +} + +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame{ + border: 1px solid @m_baseTxtColor; +} + +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: @m_titleTxtColor; +} + +#PsdSettingsGroupBox { + border: 1px solid @m_selectedBG; +} + +#FileDoesNotExistLabel { + color: rgb(255,50,50); +} + +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked { + image: url("@{image_url}/minus.png"); + } +} + +ParamsPage { + qproperty-TextColor: @m_baseTxtColor; +} + +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea +{ + border:0px; +} + +#FunctionSegmentViewer +{ + .base_inset; + border-width: 2px; +} + +#xsheetArea, #ScrollArea +{ + .base_inset(10%); + border-width: 2px; +} + +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: rgb(230,230,120); +} +#xsheetColumnAreaMenu_Lock { + background-color: rgb(245,245,245); +} +#xsheetColumnAreaMenu_Camstand { + background-color: rgb(255,164,128); +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; + &:selected { + background-color: rgb(0,0,128); + } +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + .baseBG(light, 10%); + border: 1px solid black; + + &:vertical { + width: 18px; + .set_margin( 0px, 20px ); + } + &:horizontal { + height: 18px; + .set_margin( 20px, 0px ); + } + + &::handle { + border-width: 4; + image-position: center center; + &:vertical { + border-image: url("@{image_url}/sb_g_vhandle.png")4; + image: url("@{image_url}/sb_g_vline.png"); + min-height: 40px; + } + + &:horizontal { + border-image: url("@{image_url}/sb_g_hhandle.png")4; + image: url("@{image_url}/sb_g_hline.png"); + min-width: 40px; + } + } + /* buttons */ + &::add-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; + &:pressed { + image: url("@{image_url}/sb_g_downarrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + &:pressed{ + image: url("@{image_url}/sb_g_rarrow_pressed.png"); + } + } + } + + &::sub-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; + &:pressed { + image: url("@{image_url}/sb_g_uparrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + &:pressed{ + image: url("@{image_url}/sb_g_larrow_pressed.png"); + } + } + } + + &::add-page { + background: none; + } +} + +XsheetViewer { + qproperty-TextColor: rgb(230,230,230); + qproperty-BGColor: rgb(72,72,72); + qproperty-LightLineColor: rgb(32,32,32); + qproperty-MarkerLineColor: rgb(30, 150, 196); + qproperty-PreviewFrameTextColor: rgb(150, 230, 230); + qproperty-CurrentRowBgColor: rgb(80,96,130); + + qproperty-EmptyColumnHeadColor: rgb(96,96,96); + qproperty-SelectedColumnTextColor: rgb(230, 100, 100); + + qproperty-EmptyCellColor: rgb(64,64,64); + qproperty-NotEmptyColumnColor: rgb(72,72,72); + qproperty-SelectedEmptyCellColor: rgb(108,108,108); + + qproperty-LevelColumnColor: rgb(76,110,76); + qproperty-LevelColumnBorderColor: rgb(143,179,143); + qproperty-SelectedLevelColumnColor: rgb(107,140,107); + + qproperty-VectorColumnColor: rgb(123,123,76); + qproperty-VectorColumnBorderColor: rgb(187,187,154); + qproperty-SelectedVectorColumnColor: rgb(140,140,96); + + qproperty-ChildColumnColor: rgb(106,82,107); + qproperty-ChildColumnBorderColor: rgb(177,163,179); + qproperty-SelectedChildColumnColor: rgb(122,97,122); + + qproperty-FullcolorColumnColor: rgb(101,122,150); + qproperty-FullcolorColumnBorderColor: rgb(158,184,187); + qproperty-SelectedFullcolorColumnColor: rgb(136,150,167); + + qproperty-FxColumnColor: rgb(86,85,60); + qproperty-FxColumnBorderColor: rgb(149,149,138); + qproperty-SelectedFxColumnColor: rgb(106,109,90); + + qproperty-ReferenceColumnColor: rgb(97,97,97); + qproperty-ReferenceColumnBorderColor: rgb(162,162,162); + qproperty-SelectedReferenceColumnColor: rgb(130,130,130); + + qproperty-PaletteColumnColor: rgb(58,101,95); + qproperty-PaletteColumnBorderColor: rgb(134,172,167); + qproperty-SelectedPaletteColumnColor: rgb(95,133,129); + + qproperty-ColumnHeadPastelizer: rgb(0,0,0); + qproperty-SelectedColumnHead: rgb(80,96,130); + + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-DarkLineColor: rgb(150,150,150); +} + +/*------- Schematic ---------*/ +#SchematicBottomFrame +{ + margin: 0px; + padding: 0px; + .base_outset; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer +{ + background-color: rgb(55,55,55); +} + +/*------ Function Editor ---------*/ + +#FunctionParametersPanel +{ + border: 1px solid @m_baseTxtColor; +} +#FunctionEditorTree,#ShortcutTree +{ + &::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree17_vline.png") 0; + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-more.png") 0; + } + } + + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open.png"); + } + } + } + } +} + +FunctionPanel { + qproperty-BGColor: rgb(48,48,48); + qproperty-ValueLineColor: rgb(72,72,72); + qproperty-FrameLineColor: rgb(96,96,96); + qproperty-OtherCurvesColor: rgb(128,128,128); + qproperty-RulerBackground: rgb(48,48,48); + qproperty-TextColor: rgb(230,230,230); + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} + +FunctionTreeView { + qproperty-TextColor: rgb(230,230,230); + qproperty-CurrentTextColor: rgb(230, 100, 100); +} + +SpreadsheetViewer { + qproperty-LightLightBGColor: rgb(64,64,64); + qproperty-CurrentRowBgColor: rgb(80,96,130); + qproperty-LightLineColor: rgb(32,32,32); + qproperty-MarkerLineColor: rgb(30, 150, 196); + qproperty-BGColor: rgb(72,72,72); + qproperty-VerticalLineColor: rgb(120,120,120); + qproperty-KeyFrameColor: rgb(153,93,29); + qproperty-KeyFrameBorderColor: rgb(201,176,75); + qproperty-SelectedKeyFrameColor: rgb(151,128,86); + qproperty-InBetweenColor: rgb(102,98,80); + qproperty-InBetweenBorderColor: rgb(205,206,200); + qproperty-SelectedInBetweenColor: rgb(126,128,121); + qproperty-SelectedEmptyColor: rgb(108,108,108); + qproperty-SelectedSceneRangeEmptyColor: rgb(117,117,117); + qproperty-TextColor: rgb(230,230,230); + qproperty-ColumnHeaderBorderColor: rgb(142,142,142); + qproperty-SelectedColumnTextColor: rgb(230, 100, 100); +} +#keyFrameNavigator +{ + border: 0px; + margin: 0px; + padding: 0px; +} + +#ExpressionField +{ + .base_inset(light, 50%); + border-width: 2px; + border-radius: 2px; + margin: 0px; +} + +#FunctionSegmentViewerLinkButton +{ + border: 2px; + margin: 0px; + background-image: url("@{image_url}/segment_unlinked.png"); + .base_outset(light,20%); + &:checked { + background-image: url("@{image_url}/segment_linked.png"); + .base_inset(light,20%); + } + &:disabled{ + background-image: url("@{image_url}/segment_disabled.png"); + .base_outset(light,10%); + border: 1px; + } +} + +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + .base_inset(10%); + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel +{ + color: @m_titleTxtColor; +} + +/*------ Cleanup Settings------*/ + +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} + +#CameraSettingsButton +{ + padding: 2px; + border: 0px; +} + +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } +} + +#CameraSettingsDPI{ + color: @m_titleTxtColor; +} + +#CameraSettingsRadioButton_Small { + padding: 2px; + &::indicator { + width: 11px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock_small.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock_small.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover_small.png"); + } + } + } +} + +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("@{image_url}/fsp_released.png"); + + &:hover { + image: url("@{image_url}/fsp_hover.png"); + } + &:checked { + image: url("@{image_url}/fsp_pressed.png"); + } +} + +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; /*space between button and text*/ + &::indicator { + border-width: 0px; + width: 21px; + height: 21px; + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_unlock_hover.png"); + } + } + &:checked { + image: url("@{image_url}/cam_lock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } + } +} + +/*------ Topbar and Menubar of the MainWindow ------*/ + +#TopBar { + height: 22px; + .baseBG(5%); + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + .baseBG; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("@{image_url}/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; + &::tab { + .set_margin(5px, 1px); + .set_padding(8px, 1px); + .baseBG( light, 5% ); + border: 1px solid white; + &:selected { + background-color: rgb(90,140,120); + } + &:hover { + background-color: rgb(120,120,90); + } + } +} +#StackedMenuBar +{ + background: rgb(160,160,160); + margin: 0px; + border: 0px; + padding: 0px; +} + +#DockSeparator{ + .base_outset(light, 10%); + //border-image: url("@{image_url}/dock_handle_border.png") 2; + border-width: 1; +} + +#TDockPlaceholder { + background-color: rgb(185,240,0,255); +} + +/*------ Popups -------*/ + +QDialog #dialogButtonFrame { + .baseBG(10%); +} + +#SceneSettings QLabel +{ + color: @m_titleTxtColor; +} +#PreferencesPopup QListWidget +{ + .base_inset; + border-width: 2px; + alternate-background-color: lighten(@m_baseBG, 5%); + font-size: 14px; + &::item{ + padding: 3px; + &:selected{ + background-color: @m_selectedBG; + color : black; + } + &:hover{ + background-color: lighten(@m_baseBG, 10%); + } + } +} +#OutputSettingsBox { + border:1px solid @m_selectedBG; +} + +#OutputSettingsLabel { + color: @m_titleTxtColor; +} + +#OutputSettingsCameraBox { + .base_inset; + border-width: 2px; +} + +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked + { + image: url("@{image_url}/minus.png"); + } +} + +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: rgb(155,155,155); + qproperty-DarkLineColor: rgb(47,47,47); + qproperty-HandleLeftPixmap: url("@{image_url}/h_slider_left.png"); + qproperty-HandleRightPixmap: url("@{image_url}/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("@{image_url}/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("@{image_url}/h_slider_right_disabled.png"); +} + +#FxSettingsLabel{ + color: rgb(160,230,128); +} + +#FxSettings{ + border-width: 0px; + border-bottom: 3px double rgb(64,64,64); +} + +#FxSettingsHelpButton{ + color: rgb(160,200,255); +} + +#MatchLineButton { + .baseBG(light, 10%); + &::pressed + { + .baseBG(light, 30%); + } +} \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072.qss b/stuff/config/qss/gray_072/gray_072.qss new file mode 100644 index 0000000..b6d9d51 --- /dev/null +++ b/stuff/config/qss/gray_072/gray_072.qss @@ -0,0 +1,1298 @@ +/* LESS Definitions */ +/*Image URL*/ +/*Text Color*/ +/*Used in Dialog border*/ +/*Color for Selected Item*/ +/*Color for title texts*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QWidget { + color: #e6e6e6; + background-color: #484848; +} +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #484848; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + alternate-background-color: #555555; +} +QTreeWidget::item:selected { + background-color: #80a0dc; + color: black; +} +QTreeWidget::item { + color: #e6e6e6; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background-color: #3b3b3b; +} +QMenuBar::item:selected { + background-color: #3b3b3b; + border-style: inset; + border-left-color: #131313; + border-top-color: #000000; + border-right-color: #5f5f5f; + border-bottom-color: #737373; + border-width: 1px; +} +QMenu { + background-color: #3b3b3b; +} +QMenu::item:selected { + background: #80a0dc; + color: black; +} +QMenu::item:disabled { + background-color: #555555; + color: #808080; +} +QMenu::item:disabled:selected { + background: #6c7680; +} +QMenu::separator { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + margin-left: 5px; + margin-right: 5px; + margin-top: 2px; + margin-bottom: 2px; + border-width: 1px; + height: 0px; +} +QToolBar { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + image: url("qss/gray_072/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_072/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #484848; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_072/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_072/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + background-color: #555555; + color: #808080; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ +} +QToolBar QLabel { + background-color: #484848; + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QLineEdit { + /*darken little bit*/ + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + border-radius: 2px; +} +QLineEdit:disabled { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; + color: #808080; +} +QComboBox { + /*darken little bit*/ + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + /*arrow button*/ + /*arrow button triangle*/ +} +QComboBox::drop-down { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 2px; + /*pressed state*/ +} +QComboBox::drop-down:on { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; +} +QComboBox::down-arrow { + image: url("qss/gray_072/imgs/combo_down_arrow.png"); +} +QComboBox:disabled { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; + color: #808080; +} +QPushButton { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + border-radius: 4px; + padding-left: 20px; + padding-right: 20px; + padding-top: 3px; + padding-bottom: 3px; + /*lighten lilttle bit when hover*/ + /*lighten lilttle bit when pressed*/ +} +QPushButton:hover { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; +} +QPushButton:hover:pressed { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; +} +QPushButton:checked { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; +} +QPushButton:disabled { + background-color: #555555; + border-style: outset; + border-left-color: #797979; + border-top-color: #8d8d8d; + border-right-color: #2d2d2d; + border-bottom-color: #0d0d0d; + color: #505050; +} +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +QCheckBox:hover { + background-color: #626262; +} +QCheckBox:disabled { + color: #505050; +} +QCheckBox::indicator { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 2px; +} +QCheckBox::indicator:disabled { + background-color: #555555; + border-style: inset; + border-left-color: #2d2d2d; + border-top-color: #0d0d0d; + border-right-color: #797979; + border-bottom-color: #8d8d8d; +} +QCheckBox::indicator:checked { + image: url("qss/gray_072/imgs/check_indicator.png"); +} +QCheckBox::indicator:checked:disabled { + image: url("qss/gray_072/imgs/check_indicator_disabled.png"); +} +QSlider::groove:horizontal { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + height: 1px; + margin: 1px; +} +QSlider::handle:horizontal { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border-width: 2px; + width: 5px; + margin: -8px 0px; + /* expand outside the groove */ +} +QGroupBox { + border: 1px solid #e6e6e6; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} +QSplitter::handle { + background-color: #888888; +} +/* ------ Toonz Classes Difinitions ------ */ +TPanel { + /*Used for dialog border*/ + background-color: #202020; +} +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #222222; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #222222; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #3b3b3b; + border-style: outset; + border-left-color: #5f5f5f; + border-top-color: #737373; + border-right-color: #131313; + border-bottom-color: #000000; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #808080; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + margin-top: 0px; + border-bottom-color: #484848; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_072/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_072/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_072/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: #e6e6e6; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #222222; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #3b3b3b; + border-style: outset; + border-left-color: #5f5f5f; + border-top-color: #737373; + border-right-color: #131313; + border-bottom-color: #000000; + /* for non selected tab */ + border-bottom-color: #808080; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-bottom-color: #484848; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #484848; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_072/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_072/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_072/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_072/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_072/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-top-width: 1px; + border-bottom-width: 1px; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_072/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_072/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_072/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_072/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #222222; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #808080; + margin-top: 1px; + padding-top: 3px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #808080; + margin-top: 1px; + padding-top: 3px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #626262; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_072/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_072/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_072/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_072/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_072/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-baseColor: #626262; +} +Ruler { + qproperty-ParentBGColor: #303030; + qproperty-ScaleColor: #e6e6e6; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; +} +#RulerToolOptionValues { + color: black; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #555555; + border-width: 1px; + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: #e6e6e6; + qproperty-AlternateBackground: #555555; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedItemBackground: #80a0dc; +} +DvDirTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedFolderTextColor: #001e00; + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: #555555; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #e6e6e6; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #a8bee7; +} +#PsdSettingsGroupBox { + border: 1px solid #80a0dc; +} +#FileDoesNotExistLabel { + color: #ff3232; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: #e6e6e6; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#FunctionSegmentViewer { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; +} +#xsheetArea, +#ScrollArea { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 2px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #626262; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_072/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_072/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_072/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_072/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: #e6e6e6; + qproperty-BGColor: #484848; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-PreviewFrameTextColor: #96e6e6; + qproperty-CurrentRowBgColor: #506082; + qproperty-EmptyColumnHeadColor: #606060; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #404040; + qproperty-NotEmptyColumnColor: #484848; + qproperty-SelectedEmptyCellColor: #6c6c6c; + qproperty-LevelColumnColor: #4c6e4c; + qproperty-LevelColumnBorderColor: #8fb38f; + qproperty-SelectedLevelColumnColor: #6b8c6b; + qproperty-VectorColumnColor: #7b7b4c; + qproperty-VectorColumnBorderColor: #bbbb9a; + qproperty-SelectedVectorColumnColor: #8c8c60; + qproperty-ChildColumnColor: #6a526b; + qproperty-ChildColumnBorderColor: #b1a3b3; + qproperty-SelectedChildColumnColor: #7a617a; + qproperty-FullcolorColumnColor: #657a96; + qproperty-FullcolorColumnBorderColor: #9eb8bb; + qproperty-SelectedFullcolorColumnColor: #8896a7; + qproperty-FxColumnColor: #56553c; + qproperty-FxColumnBorderColor: #95958a; + qproperty-SelectedFxColumnColor: #6a6d5a; + qproperty-ReferenceColumnColor: #616161; + qproperty-ReferenceColumnBorderColor: #a2a2a2; + qproperty-SelectedReferenceColumnColor: #828282; + qproperty-PaletteColumnColor: #3a655f; + qproperty-PaletteColumnBorderColor: #86aca7; + qproperty-SelectedPaletteColumnColor: #5f8581; + qproperty-ColumnHeadPastelizer: #000000; + qproperty-SelectedColumnHead: #506082; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #e6e6e6; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #303030; + qproperty-ValueLineColor: #484848; + qproperty-FrameLineColor: #606060; + qproperty-OtherCurvesColor: #808080; + qproperty-RulerBackground: #303030; + qproperty-TextColor: #e6e6e6; + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} +FunctionTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-CurrentTextColor: #e66464; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #404040; + qproperty-CurrentRowBgColor: #506082; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-BGColor: #484848; + qproperty-VerticalLineColor: #787878; + qproperty-KeyFrameColor: #995d1d; + qproperty-KeyFrameBorderColor: #c9b04b; + qproperty-SelectedKeyFrameColor: #978056; + qproperty-InBetweenColor: #666250; + qproperty-InBetweenBorderColor: #cdcec8; + qproperty-SelectedInBetweenColor: #7e8079; + qproperty-SelectedEmptyColor: #6c6c6c; + qproperty-SelectedSceneRangeEmptyColor: #757575; + qproperty-TextColor: #e6e6e6; + qproperty-ColumnHeaderBorderColor: #8e8e8e; + qproperty-SelectedColumnTextColor: #e66464; +} +#keyFrameNavigator { + border: 0px; + margin: 0px; + padding: 0px; +} +#ExpressionField { + background-color: #c8c8c8; + border-style: inset; + border-left-color: #a0a0a0; + border-top-color: #808080; + border-right-color: #ececec; + border-bottom-color: #ffffff; + border-width: 2px; + border-radius: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + background-image: url("qss/gray_072/imgs/segment_unlinked.png"); + background-color: #7b7b7b; + border-style: outset; + border-left-color: #9f9f9f; + border-top-color: #b3b3b3; + border-right-color: #535353; + border-bottom-color: #333333; +} +#FunctionSegmentViewerLinkButton:checked { + background-image: url("qss/gray_072/imgs/segment_linked.png"); + background-color: #7b7b7b; + border-style: inset; + border-left-color: #535353; + border-top-color: #333333; + border-right-color: #9f9f9f; + border-bottom-color: #b3b3b3; +} +#FunctionSegmentViewerLinkButton:disabled { + background-image: url("qss/gray_072/imgs/segment_disabled.png"); + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #a8bee7; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #a8bee7; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_072/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_072/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_072/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #3b3b3b; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background-color: #484848; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_072/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #555555; + border: 1px solid white; +} +#TopBarTab::tab:selected { + background-color: #5a8c78; +} +#TopBarTab::tab:hover { + background-color: #78785a; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border-width: 1; +} +#TDockPlaceholder { + background-color: #b9f000; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #2f2f2f; +} +#SceneSettings QLabel { + color: #a8bee7; +} +#PreferencesPopup QListWidget { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; + alternate-background-color: #555555; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #80a0dc; + color: black; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #626262; +} +#OutputSettingsBox { + border: 1px solid #80a0dc; +} +#OutputSettingsLabel { + color: #a8bee7; +} +#OutputSettingsCameraBox { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: #9b9b9b; + qproperty-DarkLineColor: #2f2f2f; + qproperty-HandleLeftPixmap: url("qss/gray_072/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_072/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_072/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_072/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #a0e680; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + color: #a0c8ff; +} +#MatchLineButton { + background-color: #626262; +} +#MatchLineButton::pressed { + background-color: #949494; +} + +//# sourceMappingURL=gray_072.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_072/gray_072_mac.qss b/stuff/config/qss/gray_072/gray_072_mac.qss new file mode 100644 index 0000000..b6d9d51 --- /dev/null +++ b/stuff/config/qss/gray_072/gray_072_mac.qss @@ -0,0 +1,1298 @@ +/* LESS Definitions */ +/*Image URL*/ +/*Text Color*/ +/*Used in Dialog border*/ +/*Color for Selected Item*/ +/*Color for title texts*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QWidget { + color: #e6e6e6; + background-color: #484848; +} +QFrame { + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #484848; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; + color: black; +} +QTreeWidget { + border-width: 1px; + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + alternate-background-color: #555555; +} +QTreeWidget::item:selected { + background-color: #80a0dc; + color: black; +} +QTreeWidget::item { + color: #e6e6e6; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background-color: #3b3b3b; +} +QMenuBar::item:selected { + background-color: #3b3b3b; + border-style: inset; + border-left-color: #131313; + border-top-color: #000000; + border-right-color: #5f5f5f; + border-bottom-color: #737373; + border-width: 1px; +} +QMenu { + background-color: #3b3b3b; +} +QMenu::item:selected { + background: #80a0dc; + color: black; +} +QMenu::item:disabled { + background-color: #555555; + color: #808080; +} +QMenu::item:disabled:selected { + background: #6c7680; +} +QMenu::separator { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + margin-left: 5px; + margin-right: 5px; + margin-top: 2px; + margin-bottom: 2px; + border-width: 1px; + height: 0px; +} +QToolBar { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + image: url("qss/gray_072/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_072/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #484848; + /*margin: 2px 1px 1px 1px;*/ + margin: 3px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_072/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_072/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + background-color: #555555; + color: #808080; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + /*background-color: rgb(160,160,160);*/ +} +QToolBar QLabel { + background-color: #484848; + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QLineEdit { + /*darken little bit*/ + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + border-radius: 2px; +} +QLineEdit:disabled { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; + color: #808080; +} +QComboBox { + /*darken little bit*/ + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + /*arrow button*/ + /*arrow button triangle*/ +} +QComboBox::drop-down { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 2px; + /*pressed state*/ +} +QComboBox::drop-down:on { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; +} +QComboBox::down-arrow { + image: url("qss/gray_072/imgs/combo_down_arrow.png"); +} +QComboBox:disabled { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; + color: #808080; +} +QPushButton { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + border-radius: 4px; + padding-left: 20px; + padding-right: 20px; + padding-top: 3px; + padding-bottom: 3px; + /*lighten lilttle bit when hover*/ + /*lighten lilttle bit when pressed*/ +} +QPushButton:hover { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; +} +QPushButton:hover:pressed { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; +} +QPushButton:checked { + background-color: #626262; + border-style: inset; + border-left-color: #3a3a3a; + border-top-color: #1a1a1a; + border-right-color: #868686; + border-bottom-color: #9a9a9a; +} +QPushButton:disabled { + background-color: #555555; + border-style: outset; + border-left-color: #797979; + border-top-color: #8d8d8d; + border-right-color: #2d2d2d; + border-bottom-color: #0d0d0d; + color: #505050; +} +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +QCheckBox:hover { + background-color: #626262; +} +QCheckBox:disabled { + color: #505050; +} +QCheckBox::indicator { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 2px; +} +QCheckBox::indicator:disabled { + background-color: #555555; + border-style: inset; + border-left-color: #2d2d2d; + border-top-color: #0d0d0d; + border-right-color: #797979; + border-bottom-color: #8d8d8d; +} +QCheckBox::indicator:checked { + image: url("qss/gray_072/imgs/check_indicator.png"); +} +QCheckBox::indicator:checked:disabled { + image: url("qss/gray_072/imgs/check_indicator_disabled.png"); +} +QSlider::groove:horizontal { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + height: 1px; + margin: 1px; +} +QSlider::handle:horizontal { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border-width: 2px; + width: 5px; + margin: -8px 0px; + /* expand outside the groove */ +} +QGroupBox { + border: 1px solid #e6e6e6; + margin-left: 5px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 3px; + padding-right: 3px; + padding-top: 5px; + padding-bottom: 5px; +} +QGroupBox::title { + subcontrol-origin: margin; + padding: 0px; + margin-top: -4px; + /*bottom: 3px;*/ + left: 15px; +} +QSplitter::handle { + background-color: #888888; +} +/* ------ Toonz Classes Difinitions ------ */ +TPanel { + /*Used for dialog border*/ + background-color: #202020; +} +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #222222; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #222222; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #3b3b3b; + border-style: outset; + border-left-color: #5f5f5f; + border-top-color: #737373; + border-right-color: #131313; + border-bottom-color: #000000; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #808080; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + margin-top: 0px; + border-bottom-color: #484848; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_072/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_072/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_072/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: #e6e6e6; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #222222; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #3b3b3b; + border-style: outset; + border-left-color: #5f5f5f; + border-top-color: #737373; + border-right-color: #131313; + border-bottom-color: #000000; + /* for non selected tab */ + border-bottom-color: #808080; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-bottom-color: #484848; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #484848; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_072/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_072/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_072/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_072/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_072/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-top-width: 1px; + border-bottom-width: 1px; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_072/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_072/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_072/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_072/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #222222; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #808080; + margin-top: 1px; + padding-top: 3px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #808080; + margin-top: 1px; + padding-top: 3px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #626262; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_072/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_072/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_072/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_072/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_072/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_072/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-baseColor: #626262; +} +Ruler { + qproperty-ParentBGColor: #303030; + qproperty-ScaleColor: #e6e6e6; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; +} +#RulerToolOptionValues { + color: black; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #555555; + border-width: 1px; + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: #e6e6e6; + qproperty-AlternateBackground: #555555; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedItemBackground: #80a0dc; +} +DvDirTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-SelectedTextColor: black; + qproperty-FolderTextColor: #96e6e6; + qproperty-SelectedFolderTextColor: #001e00; + qproperty-SelectedItemBackground: #80a0dc; + alternate-background-color: #555555; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #e6e6e6; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #a8bee7; +} +#PsdSettingsGroupBox { + border: 1px solid #80a0dc; +} +#FileDoesNotExistLabel { + color: #ff3232; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: #e6e6e6; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#FunctionSegmentViewer { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; +} +#xsheetArea, +#ScrollArea { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 2px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + color: black; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #626262; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_072/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_072/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_072/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_072/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_072/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_072/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_072/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_072/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: #e6e6e6; + qproperty-BGColor: #484848; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-PreviewFrameTextColor: #96e6e6; + qproperty-CurrentRowBgColor: #506082; + qproperty-EmptyColumnHeadColor: #606060; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #404040; + qproperty-NotEmptyColumnColor: #484848; + qproperty-SelectedEmptyCellColor: #6c6c6c; + qproperty-LevelColumnColor: #4c6e4c; + qproperty-LevelColumnBorderColor: #8fb38f; + qproperty-SelectedLevelColumnColor: #6b8c6b; + qproperty-VectorColumnColor: #7b7b4c; + qproperty-VectorColumnBorderColor: #bbbb9a; + qproperty-SelectedVectorColumnColor: #8c8c60; + qproperty-ChildColumnColor: #6a526b; + qproperty-ChildColumnBorderColor: #b1a3b3; + qproperty-SelectedChildColumnColor: #7a617a; + qproperty-FullcolorColumnColor: #657a96; + qproperty-FullcolorColumnBorderColor: #9eb8bb; + qproperty-SelectedFullcolorColumnColor: #8896a7; + qproperty-FxColumnColor: #56553c; + qproperty-FxColumnBorderColor: #95958a; + qproperty-SelectedFxColumnColor: #6a6d5a; + qproperty-ReferenceColumnColor: #616161; + qproperty-ReferenceColumnBorderColor: #a2a2a2; + qproperty-SelectedReferenceColumnColor: #828282; + qproperty-PaletteColumnColor: #3a655f; + qproperty-PaletteColumnBorderColor: #86aca7; + qproperty-SelectedPaletteColumnColor: #5f8581; + qproperty-ColumnHeadPastelizer: #000000; + qproperty-SelectedColumnHead: #506082; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #484848; + border-style: outset; + border-left-color: #6c6c6c; + border-top-color: #808080; + border-right-color: #202020; + border-bottom-color: #000000; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #e6e6e6; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_072/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_072/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_072/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #303030; + qproperty-ValueLineColor: #484848; + qproperty-FrameLineColor: #606060; + qproperty-OtherCurvesColor: #808080; + qproperty-RulerBackground: #303030; + qproperty-TextColor: #e6e6e6; + qproperty-SubColor: black; + qproperty-SelectedColor: #a8bee7; +} +FunctionTreeView { + qproperty-TextColor: #e6e6e6; + qproperty-CurrentTextColor: #e66464; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #404040; + qproperty-CurrentRowBgColor: #506082; + qproperty-LightLineColor: #202020; + qproperty-MarkerLineColor: #1e96c4; + qproperty-BGColor: #484848; + qproperty-VerticalLineColor: #787878; + qproperty-KeyFrameColor: #995d1d; + qproperty-KeyFrameBorderColor: #c9b04b; + qproperty-SelectedKeyFrameColor: #978056; + qproperty-InBetweenColor: #666250; + qproperty-InBetweenBorderColor: #cdcec8; + qproperty-SelectedInBetweenColor: #7e8079; + qproperty-SelectedEmptyColor: #6c6c6c; + qproperty-SelectedSceneRangeEmptyColor: #757575; + qproperty-TextColor: #e6e6e6; + qproperty-ColumnHeaderBorderColor: #8e8e8e; + qproperty-SelectedColumnTextColor: #e66464; +} +#keyFrameNavigator { + border: 0px; + margin: 0px; + padding: 0px; +} +#ExpressionField { + background-color: #c8c8c8; + border-style: inset; + border-left-color: #a0a0a0; + border-top-color: #808080; + border-right-color: #ececec; + border-bottom-color: #ffffff; + border-width: 2px; + border-radius: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + background-image: url("qss/gray_072/imgs/segment_unlinked.png"); + background-color: #7b7b7b; + border-style: outset; + border-left-color: #9f9f9f; + border-top-color: #b3b3b3; + border-right-color: #535353; + border-bottom-color: #333333; +} +#FunctionSegmentViewerLinkButton:checked { + background-image: url("qss/gray_072/imgs/segment_linked.png"); + background-color: #7b7b7b; + border-style: inset; + border-left-color: #535353; + border-top-color: #333333; + border-right-color: #9f9f9f; + border-bottom-color: #b3b3b3; +} +#FunctionSegmentViewerLinkButton:disabled { + background-image: url("qss/gray_072/imgs/segment_disabled.png"); + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #2f2f2f; + border-style: inset; + border-left-color: #060606; + border-top-color: #000000; + border-right-color: #525252; + border-bottom-color: #676767; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #a8bee7; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #a8bee7; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_072/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_072/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_072/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_072/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_072/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_072/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_072/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #3b3b3b; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background-color: #484848; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_072/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #555555; + border: 1px solid white; +} +#TopBarTab::tab:selected { + background-color: #5a8c78; +} +#TopBarTab::tab:hover { + background-color: #78785a; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #626262; + border-style: outset; + border-left-color: #868686; + border-top-color: #9a9a9a; + border-right-color: #3a3a3a; + border-bottom-color: #1a1a1a; + border-width: 1; +} +#TDockPlaceholder { + background-color: #b9f000; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #2f2f2f; +} +#SceneSettings QLabel { + color: #a8bee7; +} +#PreferencesPopup QListWidget { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; + alternate-background-color: #555555; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #80a0dc; + color: black; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #626262; +} +#OutputSettingsBox { + border: 1px solid #80a0dc; +} +#OutputSettingsLabel { + color: #a8bee7; +} +#OutputSettingsCameraBox { + background-color: #484848; + border-style: inset; + border-left-color: #202020; + border-top-color: #000000; + border-right-color: #6c6c6c; + border-bottom-color: #808080; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_072/imgs/handle_border.png") 5; + image: url("qss/gray_072/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_072/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: #9b9b9b; + qproperty-DarkLineColor: #2f2f2f; + qproperty-HandleLeftPixmap: url("qss/gray_072/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_072/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_072/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_072/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #a0e680; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + color: #a0c8ff; +} +#MatchLineButton { + background-color: #626262; +} +#MatchLineButton::pressed { + background-color: #949494; +} + +//# sourceMappingURL=gray_072.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_072/imgs/bottomseparator.png b/stuff/config/qss/gray_072/imgs/bottomseparator.png new file mode 100644 index 0000000..1d8c230 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/bottomseparator.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_lock.png b/stuff/config/qss/gray_072/imgs/cam_lock.png new file mode 100644 index 0000000..c90cb0d Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_lock.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_lock_hover.png b/stuff/config/qss/gray_072/imgs/cam_lock_hover.png new file mode 100644 index 0000000..cf1d4ad Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_lock_hover.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_lock_hover_small.png b/stuff/config/qss/gray_072/imgs/cam_lock_hover_small.png new file mode 100644 index 0000000..fdff819 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_lock_hover_small.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_lock_small.png b/stuff/config/qss/gray_072/imgs/cam_lock_small.png new file mode 100644 index 0000000..1520614 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_lock_small.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_unlock.png b/stuff/config/qss/gray_072/imgs/cam_unlock.png new file mode 100644 index 0000000..7dba22e Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_unlock.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_unlock_hover.png b/stuff/config/qss/gray_072/imgs/cam_unlock_hover.png new file mode 100644 index 0000000..ef39899 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_unlock_hover.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_unlock_hover_small.png b/stuff/config/qss/gray_072/imgs/cam_unlock_hover_small.png new file mode 100644 index 0000000..9eccd59 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_unlock_hover_small.png differ diff --git a/stuff/config/qss/gray_072/imgs/cam_unlock_small.png b/stuff/config/qss/gray_072/imgs/cam_unlock_small.png new file mode 100644 index 0000000..a5a5c7d Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/cam_unlock_small.png differ diff --git a/stuff/config/qss/gray_072/imgs/check_indicator.png b/stuff/config/qss/gray_072/imgs/check_indicator.png new file mode 100644 index 0000000..b02a76b Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/check_indicator.png differ diff --git a/stuff/config/qss/gray_072/imgs/check_indicator_disabled.png b/stuff/config/qss/gray_072/imgs/check_indicator_disabled.png new file mode 100644 index 0000000..d1b3301 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/check_indicator_disabled.png differ diff --git a/stuff/config/qss/gray_072/imgs/click.png b/stuff/config/qss/gray_072/imgs/click.png new file mode 100644 index 0000000..bd0a00b Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/click.png differ diff --git a/stuff/config/qss/gray_072/imgs/click_pink.png b/stuff/config/qss/gray_072/imgs/click_pink.png new file mode 100644 index 0000000..0ebbd37 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/click_pink.png differ diff --git a/stuff/config/qss/gray_072/imgs/colorslider_add.png b/stuff/config/qss/gray_072/imgs/colorslider_add.png new file mode 100644 index 0000000..77029d9 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/colorslider_add.png differ diff --git a/stuff/config/qss/gray_072/imgs/colorslider_add_pressed.png b/stuff/config/qss/gray_072/imgs/colorslider_add_pressed.png new file mode 100644 index 0000000..ce0ebeb Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/colorslider_add_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/colorslider_button_bg.png b/stuff/config/qss/gray_072/imgs/colorslider_button_bg.png new file mode 100644 index 0000000..7138969 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/colorslider_button_bg.png differ diff --git a/stuff/config/qss/gray_072/imgs/colorslider_sub.png b/stuff/config/qss/gray_072/imgs/colorslider_sub.png new file mode 100644 index 0000000..f3e3e69 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/colorslider_sub.png differ diff --git a/stuff/config/qss/gray_072/imgs/colorslider_sub_pressed.png b/stuff/config/qss/gray_072/imgs/colorslider_sub_pressed.png new file mode 100644 index 0000000..374c8b0 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/colorslider_sub_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/combo_down_arrow.png b/stuff/config/qss/gray_072/imgs/combo_down_arrow.png new file mode 100644 index 0000000..3551eeb Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/combo_down_arrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/down_arrow_black.png b/stuff/config/qss/gray_072/imgs/down_arrow_black.png new file mode 100644 index 0000000..d9fbc98 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/down_arrow_black.png differ diff --git a/stuff/config/qss/gray_072/imgs/flipmarker.png b/stuff/config/qss/gray_072/imgs/flipmarker.png new file mode 100644 index 0000000..1fc0e87 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/flipmarker.png differ diff --git a/stuff/config/qss/gray_072/imgs/flipslider.png b/stuff/config/qss/gray_072/imgs/flipslider.png new file mode 100644 index 0000000..829a740 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/flipslider.png differ diff --git a/stuff/config/qss/gray_072/imgs/fpssb_g_larrow.png b/stuff/config/qss/gray_072/imgs/fpssb_g_larrow.png new file mode 100644 index 0000000..7501bd4 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fpssb_g_larrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/fpssb_g_larrow_pressed.png b/stuff/config/qss/gray_072/imgs/fpssb_g_larrow_pressed.png new file mode 100644 index 0000000..1219556 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fpssb_g_larrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow.png b/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow.png new file mode 100644 index 0000000..b94ad6a Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow_pressed.png b/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow_pressed.png new file mode 100644 index 0000000..3acd61e Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fpssb_g_rarrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/fsp_hover.png b/stuff/config/qss/gray_072/imgs/fsp_hover.png new file mode 100644 index 0000000..5d755d5 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fsp_hover.png differ diff --git a/stuff/config/qss/gray_072/imgs/fsp_pressed.png b/stuff/config/qss/gray_072/imgs/fsp_pressed.png new file mode 100644 index 0000000..f8a85f8 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fsp_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/fsp_released.png b/stuff/config/qss/gray_072/imgs/fsp_released.png new file mode 100644 index 0000000..bf5ecfc Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/fsp_released.png differ diff --git a/stuff/config/qss/gray_072/imgs/h_slider_left.png b/stuff/config/qss/gray_072/imgs/h_slider_left.png new file mode 100644 index 0000000..c5c6388 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/h_slider_left.png differ diff --git a/stuff/config/qss/gray_072/imgs/h_slider_left_disabled.png b/stuff/config/qss/gray_072/imgs/h_slider_left_disabled.png new file mode 100644 index 0000000..6657149 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/h_slider_left_disabled.png differ diff --git a/stuff/config/qss/gray_072/imgs/h_slider_right.png b/stuff/config/qss/gray_072/imgs/h_slider_right.png new file mode 100644 index 0000000..cea8ec8 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/h_slider_right.png differ diff --git a/stuff/config/qss/gray_072/imgs/h_slider_right_disabled.png b/stuff/config/qss/gray_072/imgs/h_slider_right_disabled.png new file mode 100644 index 0000000..b08f75e Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/h_slider_right_disabled.png differ diff --git a/stuff/config/qss/gray_072/imgs/handle_border.png b/stuff/config/qss/gray_072/imgs/handle_border.png new file mode 100644 index 0000000..c51301a Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/handle_border.png differ diff --git a/stuff/config/qss/gray_072/imgs/left_arrow_black.png b/stuff/config/qss/gray_072/imgs/left_arrow_black.png new file mode 100644 index 0000000..b89cdcc Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/left_arrow_black.png differ diff --git a/stuff/config/qss/gray_072/imgs/minus.png b/stuff/config/qss/gray_072/imgs/minus.png new file mode 100644 index 0000000..b10fa0f Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/minus.png differ diff --git a/stuff/config/qss/gray_072/imgs/over.png b/stuff/config/qss/gray_072/imgs/over.png new file mode 100644 index 0000000..66bf9c7 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/over.png differ diff --git a/stuff/config/qss/gray_072/imgs/over_pressed_yellow.png b/stuff/config/qss/gray_072/imgs/over_pressed_yellow.png new file mode 100644 index 0000000..892023a Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/over_pressed_yellow.png differ diff --git a/stuff/config/qss/gray_072/imgs/over_yellow.png b/stuff/config/qss/gray_072/imgs/over_yellow.png new file mode 100644 index 0000000..fc182a6 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/over_yellow.png differ diff --git a/stuff/config/qss/gray_072/imgs/plus.png b/stuff/config/qss/gray_072/imgs/plus.png new file mode 100644 index 0000000..81ed056 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/plus.png differ diff --git a/stuff/config/qss/gray_072/imgs/right_arrow_black.png b/stuff/config/qss/gray_072/imgs/right_arrow_black.png new file mode 100644 index 0000000..8878c62 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/right_arrow_black.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_downarrow.png b/stuff/config/qss/gray_072/imgs/sb_g_downarrow.png new file mode 100644 index 0000000..cb7c0de Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_downarrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_downarrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_downarrow_pressed.png new file mode 100644 index 0000000..04fe449 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_downarrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_hhandle.png b/stuff/config/qss/gray_072/imgs/sb_g_hhandle.png new file mode 100644 index 0000000..5079b63 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_hhandle.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_hline.png b/stuff/config/qss/gray_072/imgs/sb_g_hline.png new file mode 100644 index 0000000..d23f97d Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_hline.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_larrow.png b/stuff/config/qss/gray_072/imgs/sb_g_larrow.png new file mode 100644 index 0000000..686f566 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_larrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_larrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_larrow_pressed.png new file mode 100644 index 0000000..1450404 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_larrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow.png b/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow.png new file mode 100644 index 0000000..406effb Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow_pressed.png new file mode 100644 index 0000000..70df3a6 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_ls_downarrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow.png b/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow.png new file mode 100644 index 0000000..5979c72 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow_pressed.png new file mode 100644 index 0000000..eb84176 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_ls_uparrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_ls_vhandle.png b/stuff/config/qss/gray_072/imgs/sb_g_ls_vhandle.png new file mode 100644 index 0000000..69caf8f Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_ls_vhandle.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_rarrow.png b/stuff/config/qss/gray_072/imgs/sb_g_rarrow.png new file mode 100644 index 0000000..29f6518 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_rarrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_rarrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_rarrow_pressed.png new file mode 100644 index 0000000..2fa0aef Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_rarrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_uparrow.png b/stuff/config/qss/gray_072/imgs/sb_g_uparrow.png new file mode 100644 index 0000000..16c8136 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_uparrow.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_uparrow_pressed.png b/stuff/config/qss/gray_072/imgs/sb_g_uparrow_pressed.png new file mode 100644 index 0000000..95224d9 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_uparrow_pressed.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_vhandle.png b/stuff/config/qss/gray_072/imgs/sb_g_vhandle.png new file mode 100644 index 0000000..cedfe49 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_vhandle.png differ diff --git a/stuff/config/qss/gray_072/imgs/sb_g_vline.png b/stuff/config/qss/gray_072/imgs/sb_g_vline.png new file mode 100644 index 0000000..b975666 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/sb_g_vline.png differ diff --git a/stuff/config/qss/gray_072/imgs/segment_disabled.png b/stuff/config/qss/gray_072/imgs/segment_disabled.png new file mode 100644 index 0000000..a2b7822 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/segment_disabled.png differ diff --git a/stuff/config/qss/gray_072/imgs/segment_linked.png b/stuff/config/qss/gray_072/imgs/segment_linked.png new file mode 100644 index 0000000..a8a7f45 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/segment_linked.png differ diff --git a/stuff/config/qss/gray_072/imgs/segment_unlinked.png b/stuff/config/qss/gray_072/imgs/segment_unlinked.png new file mode 100644 index 0000000..7621c82 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/segment_unlinked.png differ diff --git a/stuff/config/qss/gray_072/imgs/separator.png b/stuff/config/qss/gray_072/imgs/separator.png new file mode 100644 index 0000000..baee582 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/separator.png differ diff --git a/stuff/config/qss/gray_072/imgs/separator_h.png b/stuff/config/qss/gray_072/imgs/separator_h.png new file mode 100644 index 0000000..72cc296 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/separator_h.png differ diff --git a/stuff/config/qss/gray_072/imgs/separator_v.png b/stuff/config/qss/gray_072/imgs/separator_v.png new file mode 100644 index 0000000..32cefbc Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/separator_v.png differ diff --git a/stuff/config/qss/gray_072/imgs/topbar_bg.png b/stuff/config/qss/gray_072/imgs/topbar_bg.png new file mode 100644 index 0000000..18ad746 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/topbar_bg.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-closed.png b/stuff/config/qss/gray_072/imgs/tree17_branch-closed.png new file mode 100644 index 0000000..070bde4 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-closed.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-closed_nosib.png b/stuff/config/qss/gray_072/imgs/tree17_branch-closed_nosib.png new file mode 100644 index 0000000..e05adab Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-closed_nosib.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-end.png b/stuff/config/qss/gray_072/imgs/tree17_branch-end.png new file mode 100644 index 0000000..7b45c3f Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-end.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-more.png b/stuff/config/qss/gray_072/imgs/tree17_branch-more.png new file mode 100644 index 0000000..66cc906 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-more.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-open.png b/stuff/config/qss/gray_072/imgs/tree17_branch-open.png new file mode 100644 index 0000000..083b12c Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-open.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_branch-open_nosib.png b/stuff/config/qss/gray_072/imgs/tree17_branch-open_nosib.png new file mode 100644 index 0000000..3644846 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_branch-open_nosib.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree17_vline.png b/stuff/config/qss/gray_072/imgs/tree17_vline.png new file mode 100644 index 0000000..0dcebda Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree17_vline.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-closed.png b/stuff/config/qss/gray_072/imgs/tree_branch-closed.png new file mode 100644 index 0000000..c67766c Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-closed.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-closed_nosib.png b/stuff/config/qss/gray_072/imgs/tree_branch-closed_nosib.png new file mode 100644 index 0000000..3191881 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-closed_nosib.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-end.png b/stuff/config/qss/gray_072/imgs/tree_branch-end.png new file mode 100644 index 0000000..291f530 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-end.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-more.png b/stuff/config/qss/gray_072/imgs/tree_branch-more.png new file mode 100644 index 0000000..262a221 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-more.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-open.png b/stuff/config/qss/gray_072/imgs/tree_branch-open.png new file mode 100644 index 0000000..b8e604e Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-open.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_branch-open_nosib.png b/stuff/config/qss/gray_072/imgs/tree_branch-open_nosib.png new file mode 100644 index 0000000..66768b9 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_branch-open_nosib.png differ diff --git a/stuff/config/qss/gray_072/imgs/tree_vline.png b/stuff/config/qss/gray_072/imgs/tree_vline.png new file mode 100644 index 0000000..9060682 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/tree_vline.png differ diff --git a/stuff/config/qss/gray_072/imgs/up_arrow_black.png b/stuff/config/qss/gray_072/imgs/up_arrow_black.png new file mode 100644 index 0000000..6c27909 Binary files /dev/null and b/stuff/config/qss/gray_072/imgs/up_arrow_black.png differ diff --git a/stuff/config/qss/gray_128/gray_128.less b/stuff/config/qss/gray_128/gray_128.less new file mode 100644 index 0000000..0834fb1 --- /dev/null +++ b/stuff/config/qss/gray_128/gray_128.less @@ -0,0 +1,1120 @@ +/* LESS Definitions */ + +/*Image URL*/ +@base_url: "qss/gray_128"; +@image_url: "@{base_url}/imgs"; + +@m_baseBG: rgb(128,128,128); +@m_base_lightH: rgb(230,230,230); +@m_base_lightV: rgb(230,230,230); +@m_base_darkH: rgb(64,64,64); +@m_base_darkV: rgb(64,64,64); + +/* color adjustable by delta */ +.baseBG(@dark: 0%){ + background-color: darken(@m_baseBG, @dark); +} +.baseBG(light, @light: 0%){ + background-color: lighten(@m_baseBG, @light); +} + +.base_inset(@dark: 0%){ + .baseBG(@dark); + border-style: inset; + border-left-color: darken(@m_base_darkH, @dark); + border-top-color: darken(@m_base_darkV, @dark); + border-right-color: darken(@m_base_lightH, @dark); + border-bottom-color: darken(@m_base_lightV, @dark); +} +.base_inset(light, @light: 0%){ + .baseBG(light, @light); + border-style: inset; + border-left-color: lighten(@m_base_darkH, @light); + border-top-color: lighten(@m_base_darkV, @light); + border-right-color: lighten(@m_base_lightH, @light); + border-bottom-color: lighten(@m_base_lightV, @light); +} + +.base_outset(@dark: 0%){ + .baseBG(@dark); + border-style: outset; + border-left-color: darken(@m_base_lightH, @dark); + border-top-color: darken(@m_base_lightV, @dark); + border-right-color: darken(@m_base_darkH, @dark); + border-bottom-color: darken(@m_base_darkV, @dark); +} +.base_outset(light, @light: 0%){ + .baseBG(light, @light); + border-style: outset; + border-left-color: lighten(@m_base_lightH, @light); + border-top-color: lighten(@m_base_lightV, @light); + border-right-color: lighten(@m_base_darkH, @light); + border-bottom-color: lighten(@m_base_darkV, @light); +} + +/*set padding*/ +.set_padding(@hPad: 0px, @vPad: 0px){ + padding-left: @hPad; + padding-right: @hPad; + padding-top: @vPad; + padding-bottom: @vPad; +} +/*set margin*/ +.set_margin(@hMgn: 0px, @vMgn: 0px) { + margin-left: @hMgn; + margin-right: @hMgn; + margin-top: @vMgn; + margin-bottom: @vMgn; +} + +/* ------ Qt Widgets Common Difinitions ------ */ + +QFrame +{ + .baseBG; + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog +{ + .baseBG; +} + +QMainWindow::separator +{ + background: yellow; + width: 10px; /* when vertical */ + height: 10px; /* when horizontal */ +} + +QToolTip, #helpTooltip +{ + border: 1px solid black; + background-color: rgb(255,255,225); + padding: 2px; + border-radius: 2px; +} +QTreeWidget { + border-width: 1px; + .base_inset; + alternate-background-color: lighten(@m_baseBG, 5%); +} +QStatusBar { + background-color: rgb(192,192,192); + + &::item { + border-width: 0; + } + & QLabel { + background-color: rgb(192,192,192); + } + & #StatusBarLabel { + background-color: rgb(255,255,255); + .set_padding( 3px, 1px ); + } +} +QMenuBar +{ + background: rgb(160,160,160); + color: black; +} + +QMenu +{ + background: rgb(160,160,160); + &::item:selected + { + background: rgb(0,0,128); + color: white; + } +} + +QToolBar +{ + .base_outset; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; + + &::separator:horizontal { + margin_top: 1px; + margin_left: 2px; + margin_right: 2px; + image: url("@{image_url}/bottomseparator.png"); + } + &::separator:vertical { + image: url("@{image_url}/separator.png"); + } + + & QToolButton { + .baseBG; + /*margin: 2px 1px 1px 1px;*/ + margin: 2px; + padding: 0px; + border: 0px; + border-image: none; + &:hover { + border-image: url("@{image_url}/over.png") 2; + } + &:checked, + &:pressed { + border-image: url("@{image_url}/click.png") 2; + } + &:disabled{ + border-image: none; + } + &::menu-indicator + { + image: none; + } + &::menu-button { + border-image: none; + background-color: rgb(160,160,160); + } + } + + & QLabel + { + margin-top: 1px; + border-width: 2; + } + + & QToolBar + { + border-width: 0px; + } +} + +QCheckBox { + &:hover { + .baseBG(light, 10%); + } + &:disabled { + color: rgb(64,64,64); + } +} + +/* ------ Toonz Classes Difinitions ------ */ + +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer +{ + margin: 0px; + padding: 0px; + & QToolBar + { + border: 1px; + .base_outset(); + & QToolButton + { + margin: 0px; + padding: 1px; + border: 0px; + } + } + & #keyFrameNavigator + { + border: 0px; + } +} +#TabBarContainer{ + .baseBG(15%); + & #ScrollLeftButton, + & #ScrollRightButton{ + margin-top: 1px; + } +} +#PaletteTabBar, +#FxSettingsTabBar{ + .baseBG(15%); + &::tab { + .set_padding( 7px, 2px ); + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + .base_outset(light,20%); /* for non selected tab */ + margin-top: 2px; /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset(light,50%); + margin-top: 0px; + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; + } + + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#PaletteLockButton{ + &:hover{ + border-image: url("@{image_url}/over_yellow.png") 2; + } + &:checked{ + border-image: url("@{image_url}/click_pink.png") 2; + &:hover{ + border-image: url("@{image_url}/over_pressed_yellow.png") 2; + } + } +} + +#PageViewer{ + qproperty-TextColor: black; +} + +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + .baseBG(15%); + &::tab{ + .set_padding( 2px, 1px ); + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + .base_outset(light,20%); /* for non selected tab */ + border-bottom-color: @m_base_lightV; /* for non selected tab */ + + &:selected { + .base_outset(light,50%); + border-bottom-color: @m_baseBG; /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; + } + &:first:selected { + margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ + } + &:last:selected { + margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ + } + &:only-one { + margin: 0; /* if there is only one tab, we don't want overlapping margins */ + } + } +} + +#HexagonalColorWheel { + qproperty-BGColor: @m_baseBG; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider { + &::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; + } + &::handle:horizontal { + width: 8px; + margin: -8px -4px; + } +} + +#colorSliderAddButton, +#colorSliderSubButton +{ + border-image: url("@{image_url}/colorslider_button_bg.png")2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} + +#colorSliderAddButton +{ + image: url("@{image_url}/colorslider_add.png"); + &:pressed { + image: url("@{image_url}/colorslider_add_pressed.png"); + } +} + +#colorSliderSubButton +{ + image: url("@{image_url}/colorslider_sub.png"); + &:pressed { + image: url("@{image_url}/colorslider_sub_pressed.png"); + } +} + +#PlainColorPageParts +{ + border-width: 0px; + border-bottom: 1px solid black; + border-top: 1px solid white; +} + +#colorSliderLabel, +#colorSliderField +{ + font-size: 14px; +} + + +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: rgb(225,225,225); + + &:hover { + background-color: rgb(245,245,245); + } + &:pressed { + background-color: rgb(215,215,215); + } +} + +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} + +#ScrollLeftButton { + image: url("@{image_url}/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("@{image_url}/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("@{image_url}/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("@{image_url}/down_arrow_black.png"); + border-top: 1px solid black; +} + +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + .baseBG(15%); + + & #ToolBarContainer + { + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 1px; + } +} + +FlipBook #ToolBarContainer +{ + border-top: 1px solid @m_base_lightV; + margin-top: 1px; + padding-top: 1px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer +{ + & #ScrollLeftButton { + margin-top: 1px; + } + & #ScrollRightButton { + margin-top: 1px; + } +} + +#ViewerFpsSlider +{ + background-color: rgb(192,192,192); + .set_margin(19px, 0px); + border: 1px solid black; + height: 21px; + + &::handle { + border-image: url("@{image_url}/handle_border.png")6; + border-width: 6px; + image: none; + min-width: 5px; + } + &::add-line { + image: url("@{image_url}/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_rarrow_pressed.png"); + } + } + &::sub-line { + image: url("@{image_url}/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; + &:pressed { + image: url("@{image_url}/fpssb_g_larrow_pressed.png"); + } + } +} + +#FlipConsolePlayToolBar{ + border: none; + & QToolButton { + height: 14px; + } +} + +FlipSlider { + qproperty-PBHeight: 20; + + qproperty-PBOverlay: url("@{image_url}/flipslider.png"); + qproperty-PBMarker: url("@{image_url}/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-baseColor: rgb(192,192,192); + qproperty-notStartedColor: rgb(204,34,34); + qproperty-startedColor: rgb(200,128,128); + qproperty-finishedColor: rgb(192,192,192); +} + +Ruler { + qproperty-ParentBGColor: rgb(235,235,235); + qproperty-ScaleColor: black; +} + +#ComboViewerToolOptions{ + border: 1px; + .base_outset; +} + +/*-----------File Browser------------*/ +#DirTreeView, #FunctionEditorTree, #ShortcutTree, #FxTreeView +{ + alternate-background-color: lighten(@m_baseBG, 5%); + border-width: 1px; + .base_inset; + margin: 0px; +} +#DirTreeView::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree_vline.png") 0; + &:adjoins-item + { + border-image: url("@{image_url}/tree_branch-more.png") 0; + } + } + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree_branch-open.png"); + } + } + } +} + +DvItemViewerPanel { + qproperty-TextColor: black; + qproperty-AlternateBackground: #8d8d8d; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: rgb(0,0,128); +} + +DvDirTreeView { + qproperty-TextColor: black; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: rgb(0,0,128); + qproperty-SelectedFolderTextColor: yellow; + alternate-background-color: lighten(@m_baseBG, 5%); +} + +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame{ + border: 1px solid rgb(20,20,20); +} + +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: rgb(0,0,64); +} + +#PsdSettingsGroupBox { + border: 1px solid rgb(0,0,64); +} + +#FileDoesNotExistLabel { + color: rgb(128,0,0); +} + +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked { + image: url("@{image_url}/minus.png"); + } +} + +ParamsPage { + qproperty-TextColor: black; +} + +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea +{ + border:0px; +} +#xsheetArea, #ScrollArea, #FunctionSegmentViewer +{ + border-width: 2px; + .base_inset; + margin: 0px; +} + +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: rgb(230,230,120); +} +#xsheetColumnAreaMenu_Lock { + background-color: rgb(245,245,245); +} +#xsheetColumnAreaMenu_Camstand { + background-color: rgb(255,164,128); +} +#xsheetColumnAreaMenu_Preview, +#xsheetColumnAreaMenu_Lock, +#xsheetColumnAreaMenu_Camstand { + &:selected { + background-color: rgb(0,0,128); + } +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: rgb(160,160,160); + border: 1px solid black; + + &:vertical { + width: 18px; + .set_margin( 0px, 20px ); + } + &:horizontal { + height: 18px; + .set_margin( 20px, 0px ); + } + + &::handle { + border-width: 4; + image-position: center center; + &:vertical { + border-image: url("@{image_url}/sb_g_vhandle.png")4; + image: url("@{image_url}/sb_g_vline.png"); + min-height: 40px; + } + + &:horizontal { + border-image: url("@{image_url}/sb_g_hhandle.png")4; + image: url("@{image_url}/sb_g_hline.png"); + min-width: 40px; + } + } + /* buttons */ + &::add-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; + &:pressed { + image: url("@{image_url}/sb_g_downarrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + &:pressed{ + image: url("@{image_url}/sb_g_rarrow_pressed.png"); + } + } + } + + &::sub-line { + subcontrol-origin: margin; + &:vertical { + image: url("@{image_url}/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; + &:pressed { + image: url("@{image_url}/sb_g_uparrow_pressed.png"); + } + } + &:horizontal { + image: url("@{image_url}/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + &:pressed{ + image: url("@{image_url}/sb_g_larrow_pressed.png"); + } + } + } + + &::add-page { + background: none; + } +} + +XsheetViewer { + qproperty-TextColor: black; + qproperty-BGColor: rgb(164,164,164); + qproperty-LightLineColor: rgb(146,144,146); + qproperty-MarkerLineColor: rgb(0, 255, 246); + qproperty-PreviewFrameTextColor: rgb(0, 0, 255); + qproperty-CurrentRowBgColor: rgb(210,210,210); + + qproperty-EmptyColumnHeadColor: rgb(200,200,200); + qproperty-SelectedColumnTextColor: rgb(230, 100, 100); + + qproperty-EmptyCellColor: rgb(124,124,124); + qproperty-NotEmptyColumnColor: rgb(164,164,164); + qproperty-SelectedEmptyCellColor: rgb(210,210,210); + + qproperty-LevelColumnColor: rgb(127,219,127); + qproperty-LevelColumnBorderColor: rgb(47,82,47); + qproperty-SelectedLevelColumnColor: rgb(191,237,191); + + qproperty-VectorColumnColor: rgb(212,212,133); + qproperty-VectorColumnBorderColor: rgb(79,79,49); + qproperty-SelectedVectorColumnColor: rgb(234,234,194); + + qproperty-ChildColumnColor: rgb(214,154,219); + qproperty-ChildColumnBorderColor: rgb(80,57,82); + qproperty-SelectedChildColumnColor: rgb(235,205,237); + + qproperty-FullcolorColumnColor: rgb(154,214,219); + qproperty-FullcolorColumnBorderColor: rgb(57,80,82); + qproperty-SelectedFullcolorColumnColor: rgb(205,235,237); + + qproperty-FxColumnColor: rgb(130,129,93); + qproperty-FxColumnBorderColor: rgb(48,48,35); + qproperty-SelectedFxColumnColor: rgb(193,192,174); + + qproperty-ReferenceColumnColor: rgb(171,171,171); + qproperty-ReferenceColumnBorderColor: rgb(62,62,62); + qproperty-SelectedReferenceColumnColor: rgb(213,213,213); + + qproperty-PaletteColumnColor: rgb(42,171,154); + qproperty-PaletteColumnBorderColor: rgb(15,62,56); + qproperty-SelectedPaletteColumnColor: rgb(146,221,202); + + qproperty-ColumnHeadPastelizer: rgb(255,255,255); + qproperty-SelectedColumnHead: rgb(190,210,240); + + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-DarkLineColor: rgb(150,150,150); +} + +/*------- Schematic ---------*/ +#SchematicBottomFrame +{ + margin: 0px; + padding: 0px; + .base_outset; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer +{ + background-color: rgb(55,55,55); +} + +/*------ Function Editor ---------*/ + +#FunctionParametersPanel +{ + border: 1px solid rgb(20,20,20); +} +#FunctionEditorTree,#ShortcutTree +{ + &::branch { + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-end.png") 0; + } + + &:has-siblings { + border-image: url("@{image_url}/tree17_vline.png") 0; + &:adjoins-item { + border-image: url("@{image_url}/tree17_branch-more.png") 0; + } + } + + &:has-children { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed_nosib.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open_nosib.png"); + } + + &:has-siblings { + &:closed { + border-image: none; + image: url("@{image_url}/tree17_branch-closed.png"); + } + &:open { + border-image: none; + image: url("@{image_url}/tree17_branch-open.png"); + } + } + } + } +} + +FunctionPanel { + qproperty-BGColor: rgb(225,225,225); + qproperty-ValueLineColor: rgb(186,186,186); + qproperty-FrameLineColor: rgb(210,210,210); + qproperty-OtherCurvesColor: rgb(150,150,150); + qproperty-RulerBackground: rgb(255,255,255); + qproperty-TextColor: black; + qproperty-SubColor: white; + qproperty-SelectedColor: blue; +} + +FunctionTreeView { + qproperty-TextColor: black; + qproperty-CurrentTextColor: red; +} + +SpreadsheetViewer { + qproperty-LightLightBGColor: rgb(124,124,124); + qproperty-CurrentRowBgColor: rgb(210,210,210); + qproperty-LightLineColor: rgb(146,144,146); + qproperty-MarkerLineColor: rgb(0, 255, 246); + qproperty-BGColor: rgb(164,164,164); + qproperty-VerticalLineColor: rgb(0, 0, 0); + qproperty-KeyFrameColor: rgb(219,139,54); + qproperty-KeyFrameBorderColor: rgb(82,51,20); + qproperty-SelectedKeyFrameColor: rgb(237,197,155); + qproperty-InBetweenColor: rgb(194,194,176); + qproperty-InBetweenBorderColor: rgb(72,72,65); + qproperty-SelectedInBetweenColor: rgb(225,225,216); + qproperty-SelectedEmptyColor: rgb(190,190,190); + qproperty-SelectedSceneRangeEmptyColor: rgb(210,210,210); + qproperty-TextColor: black; + qproperty-ColumnHeaderBorderColor: rgb(46,47,46); + qproperty-SelectedColumnTextColor: rgb(255, 0, 0); +} +#keyFrameNavigator +{ + border: 0px; +} + +#ExpressionField +{ + .base_inset; + background-color: white; + border-width: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton +{ + border: 2px; + margin: 0px; + image: url("@{image_url}/segment_unlinked.png"); + .base_outset(light,20%); + &:checked { + image: url("@{image_url}/segment_linked.png"); + .base_inset(light,20%); + } + &:disabled{ + image: url("@{image_url}/segment_disabled.png"); + .base_outset(light,10%); + border: 1px; + } +} + + +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + .base_inset(light, 10%); + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel +{ + color: rgb(0,0,64); +} + +/*------ Cleanup Settings------*/ + +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} + +#CameraSettingsButton +{ + padding: 2px; + border: 0px; +} + +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } +} + +#CameraSettingsDPI{ + color: rgb(0,64,0); +} + +#CameraSettingsRadioButton_Small { + padding: 2px; + &::indicator { + width: 11px; + height: 21px; + &:checked { + image: url("@{image_url}/cam_lock_small.png"); + } + &:unchecked { + image: url("@{image_url}/cam_unlock_small.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover_small.png"); + } + } + } +} + +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("@{image_url}/fsp_released.png"); + + &:hover { + image: url("@{image_url}/fsp_hover.png"); + } + &:checked { + image: url("@{image_url}/fsp_pressed.png"); + } +} + +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; /*space between button and text*/ + &::indicator { + border-width: 0px; + width: 21px; + height: 21px; + &:unchecked { + image: url("@{image_url}/cam_unlock.png"); + &:hover { + image: url("@{image_url}/cam_unlock_hover.png"); + } + } + &:checked { + image: url("@{image_url}/cam_lock.png"); + &:hover { + image: url("@{image_url}/cam_lock_hover.png"); + } + } + } +} + +/*------ Topbar and Menubar of the MainWindow ------*/ + +#TopBar { + height: 22px; + background-color: rgb(192,192,192); + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background: rgb(160,160,160); + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("@{image_url}/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; + &::tab { + .set_margin(5px, 1px); + .set_padding(8px, 1px); + border: 1px solid black; + background-color: rgb(160,160,160); + &:selected { + background-color: rgb(205,220,192); + } + &:hover { + background-color: rgb(192,192,192); + } + } +} +#StackedMenuBar +{ + background: rgb(160,160,160); + margin: 0px; + border: 0px; + padding: 0px; +} + +#DockSeparator{ + .base_outset(light, 10%); + //border-image: url("@{image_url}/dock_handle_border.png") 2; + border-width: 1; +} + +/*------ Popups -------*/ + +QDialog #dialogButtonFrame { + .baseBG(10%); +} + +#SceneSettings QLabel +{ + color: rgb(0,0,64); +} +#PreferencesPopup QListWidget +{ + .base_inset(light, 20%); + border-width: 2px; + alternate-background-color: #aaaaaa; + font-size: 14px; + &::item{ + padding: 3px; + &:selected{ + background-color: rgb(0,0,128); + color : white; + } + &:hover{ + background-color: rgb(210,210,210); + } + } +} +#OutputSettingsBox { + border:1px solid rgb(0,0,64); +} + +#OutputSettingsLabel { + color: rgb(0,0,64); +} + +#OutputSettingsCameraBox { + .base_inset; + border-width: 2px; +} + +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("@{image_url}/handle_border.png")5; + image: url("@{image_url}/plus.png"); + image-position: center center; + + &:checked + { + image: url("@{image_url}/minus.png"); + } +} + +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: white; + qproperty-DarkLineColor: rgb(128,128,128); + qproperty-HandleLeftPixmap: url("@{image_url}/h_slider_left.png"); + qproperty-HandleRightPixmap: url("@{image_url}/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("@{image_url}/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("@{image_url}/h_slider_right_disabled.png"); +} + +#FxSettingsLabel{ + color: rgb(0,64,0); +} + +#FxSettings{ + border-width: 0px; + border-bottom: 3px double rgb(64,64,64); +} + +#FxSettingsHelpButton{ + background-color: rgb(192,192,192); + color: rgb(0,0,64); + &:hover { + background-color: rgb(220,220,220); + } +} \ No newline at end of file diff --git a/stuff/config/qss/gray_128/gray_128.qss b/stuff/config/qss/gray_128/gray_128.qss new file mode 100644 index 0000000..ba70e14 --- /dev/null +++ b/stuff/config/qss/gray_128/gray_128.qss @@ -0,0 +1,1038 @@ +/* LESS Definitions */ +/*Image URL*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QFrame { + background-color: #808080; + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #808080; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; +} +QTreeWidget { + border-width: 1px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + alternate-background-color: #8d8d8d; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background: #a0a0a0; + color: black; +} +QMenu { + background: #a0a0a0; +} +QMenu::item:selected { + background: #000080; + color: white; +} +QToolBar { + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + margin_top: 1px; + margin_left: 2px; + margin_right: 2px; + image: url("qss/gray_128/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_128/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #808080; + /*margin: 2px 1px 1px 1px;*/ + margin: 2px; + padding: 0px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_128/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_128/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + background-color: #a0a0a0; +} +QToolBar QLabel { + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QCheckBox:hover { + background-color: #9a9a9a; +} +QCheckBox:disabled { + color: #404040; +} +/* ------ Toonz Classes Difinitions ------ */ +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #5a5a5a; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #5a5a5a; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #e6e6e6; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #ffffff; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #c0c0c0; + border-bottom-color: #c0c0c0; + margin-top: 0px; + border-bottom-color: #808080; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_128/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_128/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_128/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: black; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #5a5a5a; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; + /* for non selected tab */ + border-bottom-color: #e6e6e6; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #ffffff; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #c0c0c0; + border-bottom-color: #c0c0c0; + border-bottom-color: #808080; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #808080; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_128/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_128/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_128/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_128/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_128/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + border-width: 0px; + border-bottom: 1px solid black; + border-top: 1px solid white; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_128/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_128/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_128/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_128/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #5a5a5a; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #e6e6e6; + margin-top: 1px; + padding-top: 1px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #e6e6e6; + margin-top: 1px; + padding-top: 1px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #c0c0c0; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_128/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_128/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_128/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_128/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_128/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_128/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_128/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-baseColor: #c0c0c0; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-finishedColor: #c0c0c0; +} +Ruler { + qproperty-ParentBGColor: #ebebeb; + qproperty-ScaleColor: black; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #8d8d8d; + border-width: 1px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_128/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_128/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_128/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: black; + qproperty-AlternateBackground: #8d8d8d; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: #000080; +} +DvDirTreeView { + qproperty-TextColor: black; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: #000080; + qproperty-SelectedFolderTextColor: yellow; + alternate-background-color: #8d8d8d; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #141414; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #000040; +} +#PsdSettingsGroupBox { + border: 1px solid #000040; +} +#FileDoesNotExistLabel { + color: #800000; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_128/imgs/handle_border.png") 5; + image: url("qss/gray_128/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_128/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: black; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#xsheetArea, +#ScrollArea, +#FunctionSegmentViewer { + border-width: 2px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + margin: 0px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #a0a0a0; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_128/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_128/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_128/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_128/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_128/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_128/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_128/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_128/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_128/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_128/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_128/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_128/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: black; + qproperty-BGColor: #a4a4a4; + qproperty-LightLineColor: #929092; + qproperty-MarkerLineColor: #00fff6; + qproperty-PreviewFrameTextColor: #0000ff; + qproperty-CurrentRowBgColor: #d2d2d2; + qproperty-EmptyColumnHeadColor: #c8c8c8; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #7c7c7c; + qproperty-NotEmptyColumnColor: #a4a4a4; + qproperty-SelectedEmptyCellColor: #d2d2d2; + qproperty-LevelColumnColor: #7fdb7f; + qproperty-LevelColumnBorderColor: #2f522f; + qproperty-SelectedLevelColumnColor: #bfedbf; + qproperty-VectorColumnColor: #d4d485; + qproperty-VectorColumnBorderColor: #4f4f31; + qproperty-SelectedVectorColumnColor: #eaeac2; + qproperty-ChildColumnColor: #d69adb; + qproperty-ChildColumnBorderColor: #503952; + qproperty-SelectedChildColumnColor: #ebcded; + qproperty-FullcolorColumnColor: #9ad6db; + qproperty-FullcolorColumnBorderColor: #395052; + qproperty-SelectedFullcolorColumnColor: #cdebed; + qproperty-FxColumnColor: #82815d; + qproperty-FxColumnBorderColor: #303023; + qproperty-SelectedFxColumnColor: #c1c0ae; + qproperty-ReferenceColumnColor: #ababab; + qproperty-ReferenceColumnBorderColor: #3e3e3e; + qproperty-SelectedReferenceColumnColor: #d5d5d5; + qproperty-PaletteColumnColor: #2aab9a; + qproperty-PaletteColumnBorderColor: #0f3e38; + qproperty-SelectedPaletteColumnColor: #92ddca; + qproperty-ColumnHeadPastelizer: #ffffff; + qproperty-SelectedColumnHead: #bed2f0; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #141414; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_128/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_128/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_128/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #e1e1e1; + qproperty-ValueLineColor: #bababa; + qproperty-FrameLineColor: #d2d2d2; + qproperty-OtherCurvesColor: #969696; + qproperty-RulerBackground: #ffffff; + qproperty-TextColor: black; + qproperty-SubColor: white; + qproperty-SelectedColor: blue; +} +FunctionTreeView { + qproperty-TextColor: black; + qproperty-CurrentTextColor: red; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #7c7c7c; + qproperty-CurrentRowBgColor: #d2d2d2; + qproperty-LightLineColor: #929092; + qproperty-MarkerLineColor: #00fff6; + qproperty-BGColor: #a4a4a4; + qproperty-VerticalLineColor: #000000; + qproperty-KeyFrameColor: #db8b36; + qproperty-KeyFrameBorderColor: #523314; + qproperty-SelectedKeyFrameColor: #edc59b; + qproperty-InBetweenColor: #c2c2b0; + qproperty-InBetweenBorderColor: #484841; + qproperty-SelectedInBetweenColor: #e1e1d8; + qproperty-SelectedEmptyColor: #bebebe; + qproperty-SelectedSceneRangeEmptyColor: #d2d2d2; + qproperty-TextColor: black; + qproperty-ColumnHeaderBorderColor: #2e2f2e; + qproperty-SelectedColumnTextColor: #ff0000; +} +#keyFrameNavigator { + border: 0px; +} +#ExpressionField { + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + background-color: white; + border-width: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + image: url("qss/gray_128/imgs/segment_unlinked.png"); + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; +} +#FunctionSegmentViewerLinkButton:checked { + image: url("qss/gray_128/imgs/segment_linked.png"); + background-color: #b3b3b3; + border-style: inset; + border-left-color: #737373; + border-top-color: #737373; + border-right-color: #ffffff; + border-bottom-color: #ffffff; +} +#FunctionSegmentViewerLinkButton:disabled { + image: url("qss/gray_128/imgs/segment_disabled.png"); + background-color: #9a9a9a; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #5a5a5a; + border-bottom-color: #5a5a5a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #9a9a9a; + border-style: inset; + border-left-color: #5a5a5a; + border-top-color: #5a5a5a; + border-right-color: #ffffff; + border-bottom-color: #ffffff; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #000040; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #004000; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_128/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_128/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_128/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #c0c0c0; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_128/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + border: 1px solid black; + background-color: #a0a0a0; +} +#TopBarTab::tab:selected { + background-color: #cddcc0; +} +#TopBarTab::tab:hover { + background-color: #c0c0c0; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #9a9a9a; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #5a5a5a; + border-bottom-color: #5a5a5a; + border-width: 1; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #676767; +} +#SceneSettings QLabel { + color: #000040; +} +#PreferencesPopup QListWidget { + background-color: #b3b3b3; + border-style: inset; + border-left-color: #737373; + border-top-color: #737373; + border-right-color: #ffffff; + border-bottom-color: #ffffff; + border-width: 2px; + alternate-background-color: #aaaaaa; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #000080; + color: white; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #d2d2d2; +} +#OutputSettingsBox { + border: 1px solid #000040; +} +#OutputSettingsLabel { + color: #000040; +} +#OutputSettingsCameraBox { + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_128/imgs/handle_border.png") 5; + image: url("qss/gray_128/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_128/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: white; + qproperty-DarkLineColor: #808080; + qproperty-HandleLeftPixmap: url("qss/gray_128/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_128/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_128/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_128/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #004000; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + background-color: #c0c0c0; + color: #000040; +} +#FxSettingsHelpButton:hover { + background-color: #dcdcdc; +} + +//# sourceMappingURL=gray_128.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_128/gray_128_mac.qss b/stuff/config/qss/gray_128/gray_128_mac.qss new file mode 100644 index 0000000..ba70e14 --- /dev/null +++ b/stuff/config/qss/gray_128/gray_128_mac.qss @@ -0,0 +1,1038 @@ +/* LESS Definitions */ +/*Image URL*/ +/* color adjustable by delta */ +/*set padding*/ +/*set margin*/ +/* ------ Qt Widgets Common Difinitions ------ */ +QFrame { + background-color: #808080; + margin: 0px; + border: 0px; + padding: 0px; +} +QDialog { + background-color: #808080; +} +QMainWindow::separator { + background: yellow; + width: 10px; + /* when vertical */ + height: 10px; + /* when horizontal */ +} +QToolTip, +#helpTooltip { + border: 1px solid black; + background-color: #ffffe1; + padding: 2px; + border-radius: 2px; +} +QTreeWidget { + border-width: 1px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + alternate-background-color: #8d8d8d; +} +QStatusBar { + background-color: #c0c0c0; +} +QStatusBar::item { + border-width: 0; +} +QStatusBar QLabel { + background-color: #c0c0c0; +} +QStatusBar #StatusBarLabel { + background-color: #ffffff; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; +} +QMenuBar { + background: #a0a0a0; + color: black; +} +QMenu { + background: #a0a0a0; +} +QMenu::item:selected { + background: #000080; + color: white; +} +QToolBar { + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; + border-width: 1px; + margin: 0px; + padding: 0px; + border-image: none; +} +QToolBar::separator:horizontal { + margin_top: 1px; + margin_left: 2px; + margin_right: 2px; + image: url("qss/gray_128/imgs/bottomseparator.png"); +} +QToolBar::separator:vertical { + image: url("qss/gray_128/imgs/separator.png"); +} +QToolBar QToolButton { + background-color: #808080; + /*margin: 2px 1px 1px 1px;*/ + margin: 2px; + padding: 0px; + border: 0px; + border-image: none; +} +QToolBar QToolButton:hover { + border-image: url("qss/gray_128/imgs/over.png") 2; +} +QToolBar QToolButton:checked, +QToolBar QToolButton:pressed { + border-image: url("qss/gray_128/imgs/click.png") 2; +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton::menu-indicator { + image: none; +} +QToolBar QToolButton::menu-button { + border-image: none; + background-color: #a0a0a0; +} +QToolBar QLabel { + margin-top: 1px; + border-width: 2; +} +QToolBar QToolBar { + border-width: 0px; +} +QCheckBox:hover { + background-color: #9a9a9a; +} +QCheckBox:disabled { + color: #404040; +} +/* ------ Toonz Classes Difinitions ------ */ +/* ------ Palette ------ */ +PaletteViewer #ToolBarContainer { + margin: 0px; + padding: 0px; +} +PaletteViewer #ToolBarContainer QToolBar { + border: 1px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; +} +PaletteViewer #ToolBarContainer QToolBar QToolButton { + margin: 0px; + padding: 1px; + border: 0px; +} +PaletteViewer #ToolBarContainer #keyFrameNavigator { + border: 0px; +} +#TabBarContainer { + background-color: #5a5a5a; +} +#TabBarContainer #ScrollLeftButton, +#TabBarContainer #ScrollRightButton { + margin-top: 1px; +} +#PaletteTabBar, +#FxSettingsTabBar { + background-color: #5a5a5a; +} +#PaletteTabBar::tab, +#FxSettingsTabBar::tab { + padding-left: 7px; + padding-right: 7px; + padding-top: 2px; + padding-bottom: 2px; + min-width: 60px; + border-width: 1px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; + /* for non selected tab */ + margin-top: 2px; + /* for non selected tab */ + border-bottom-color: #e6e6e6; + /* for non selected tab */ +} +#PaletteTabBar::tab:selected, +#FxSettingsTabBar::tab:selected { + background-color: #ffffff; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #c0c0c0; + border-bottom-color: #c0c0c0; + margin-top: 0px; + border-bottom-color: #808080; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -4px; + margin-right: -4px; +} +#PaletteTabBar::tab:first:selected, +#FxSettingsTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#PaletteTabBar::tab:last:selected, +#FxSettingsTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#PaletteTabBar::tab:only-one, +#FxSettingsTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#PaletteLockButton:hover { + border-image: url("qss/gray_128/imgs/over_yellow.png") 2; +} +#PaletteLockButton:checked { + border-image: url("qss/gray_128/imgs/click_pink.png") 2; +} +#PaletteLockButton:checked:hover { + border-image: url("qss/gray_128/imgs/over_pressed_yellow.png") 2; +} +#PageViewer { + qproperty-TextColor: black; +} +/* ------ Style Editor ------ */ +#StyleEditorTabBar { + background-color: #5a5a5a; +} +#StyleEditorTabBar::tab { + padding-left: 2px; + padding-right: 2px; + padding-top: 1px; + padding-bottom: 1px; + font-size: 12px; + min-width: 40px; + border-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; + /* for non selected tab */ + border-bottom-color: #e6e6e6; + /* for non selected tab */ +} +#StyleEditorTabBar::tab:selected { + background-color: #ffffff; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #c0c0c0; + border-bottom-color: #c0c0c0; + border-bottom-color: #808080; + /* same as the pane color */ + /* expand/overlap to the left and right by 4px */ + margin-left: -2px; + margin-right: -2px; +} +#StyleEditorTabBar::tab:first:selected { + margin-left: 0; + /* the first selected tab has nothing to overlap with on the left */ +} +#StyleEditorTabBar::tab:last:selected { + margin-right: 0; + /* the last selected tab has nothing to overlap with on the right */ +} +#StyleEditorTabBar::tab:only-one { + margin: 0; + /* if there is only one tab, we don't want overlapping margins */ +} +#HexagonalColorWheel { + qproperty-BGColor: #808080; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + height: 20; + border-image: none; + border-width: 1; + height: 1px; +} +#colorSlider::handle:horizontal { + width: 8px; + margin: -8px -4px; +} +#colorSliderAddButton, +#colorSliderSubButton { + border-image: url("qss/gray_128/imgs/colorslider_button_bg.png") 2; + padding: 0px; + margin: 0px; + border: 2px; + image-position: center center; +} +#colorSliderAddButton { + image: url("qss/gray_128/imgs/colorslider_add.png"); +} +#colorSliderAddButton:pressed { + image: url("qss/gray_128/imgs/colorslider_add_pressed.png"); +} +#colorSliderSubButton { + image: url("qss/gray_128/imgs/colorslider_sub.png"); +} +#colorSliderSubButton:pressed { + image: url("qss/gray_128/imgs/colorslider_sub_pressed.png"); +} +#PlainColorPageParts { + border-width: 0px; + border-bottom: 1px solid black; + border-top: 1px solid white; +} +#colorSliderLabel, +#colorSliderField { + font-size: 14px; +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + padding: 0px; + border-radius: 1px; + background-color: #e1e1e1; +} +DvScrollWidget > QPushButton:hover { + background-color: #f5f5f5; +} +DvScrollWidget > QPushButton:pressed { + background-color: #d7d7d7; +} +#ScrollLeftButton, +#ScrollRightButton, +#ScrollUpButton, +#ScrollDownButton { + min-width: 15px; + max-width: 15px; +} +#ScrollLeftButton { + image: url("qss/gray_128/imgs/left_arrow_black.png"); + border-right: 1px solid black; +} +#ScrollRightButton { + image: url("qss/gray_128/imgs/right_arrow_black.png"); + border-left: 1px solid black; +} +#ScrollUpButton { + image: url("qss/gray_128/imgs/up_arrow_black.png"); + border-bottom: 1px solid black; +} +#ScrollDownButton { + image: url("qss/gray_128/imgs/down_arrow_black.png"); + border-top: 1px solid black; +} +/* ------ Viewer, Flipbook ------ */ +#ViewerPanel { + background-color: #5a5a5a; +} +#ViewerPanel #ToolBarContainer { + border-top: 1px solid #e6e6e6; + margin-top: 1px; + padding-top: 1px; +} +FlipBook #ToolBarContainer { + border-top: 1px solid #e6e6e6; + margin-top: 1px; + padding-top: 1px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +#ViewerFpsSlider { + background-color: #c0c0c0; + margin-left: 19px; + margin-right: 19px; + margin-top: 0px; + margin-bottom: 0px; + border: 1px solid black; + height: 21px; +} +#ViewerFpsSlider::handle { + border-image: url("qss/gray_128/imgs/handle_border.png") 6; + border-width: 6px; + image: none; + min-width: 5px; +} +#ViewerFpsSlider::add-line { + image: url("qss/gray_128/imgs/fpssb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::add-line:pressed { + image: url("qss/gray_128/imgs/fpssb_g_rarrow_pressed.png"); +} +#ViewerFpsSlider::sub-line { + image: url("qss/gray_128/imgs/fpssb_g_larrow.png"); + width: 20px; + subcontrol-position: left; + subcontrol-origin: margin; + margin: 0px; +} +#ViewerFpsSlider::sub-line:pressed { + image: url("qss/gray_128/imgs/fpssb_g_larrow_pressed.png"); +} +#FlipConsolePlayToolBar { + border: none; +} +#FlipConsolePlayToolBar QToolButton { + height: 14px; +} +FlipSlider { + qproperty-PBHeight: 20; + qproperty-PBOverlay: url("qss/gray_128/imgs/flipslider.png"); + qproperty-PBMarker: url("qss/gray_128/imgs/flipmarker.png"); + qproperty-PBColorMarginLeft: 1; + qproperty-PBColorMarginTop: 1; + qproperty-PBColorMarginRight: 1; + qproperty-PBColorMarginBottom: 1; + qproperty-PBMarkerMarginLeft: 6; + qproperty-PBMarkerMarginRight: 6; + qproperty-baseColor: #c0c0c0; + qproperty-notStartedColor: #cc2222; + qproperty-startedColor: #c88080; + qproperty-finishedColor: #c0c0c0; +} +Ruler { + qproperty-ParentBGColor: #ebebeb; + qproperty-ScaleColor: black; +} +#ComboViewerToolOptions { + border: 1px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; +} +/*-----------File Browser------------*/ +#DirTreeView, +#FunctionEditorTree, +#ShortcutTree, +#FxTreeView { + alternate-background-color: #8d8d8d; + border-width: 1px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + margin: 0px; +} +#DirTreeView::branch:adjoins-item { + border-image: url("qss/gray_128/imgs/tree_branch-end.png") 0; +} +#DirTreeView::branch:has-siblings { + border-image: url("qss/gray_128/imgs/tree_vline.png") 0; +} +#DirTreeView::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_128/imgs/tree_branch-more.png") 0; +} +#DirTreeView::branch:has-children:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-closed_nosib.png"); +} +#DirTreeView::branch:has-children:open { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-open_nosib.png"); +} +#DirTreeView::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-closed.png"); +} +#DirTreeView::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_128/imgs/tree_branch-open.png"); +} +DvItemViewerPanel { + qproperty-TextColor: black; + qproperty-AlternateBackground: #8d8d8d; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: #000080; +} +DvDirTreeView { + qproperty-TextColor: black; + qproperty-SelectedTextColor: white; + qproperty-FolderTextColor: blue; + qproperty-SelectedItemBackground: #000080; + qproperty-SelectedFolderTextColor: yellow; + alternate-background-color: #8d8d8d; +} +/*---------------------------------------------------------------------------*/ +/* Cleanup Settings, LoadLevel, PsdSettingsPopup, FxSettingsPopup */ +/*---------------------------------------------------------------------------*/ +#CleanupSettingsFrame, +#LoadLevelFrame, +#SolidLineFrame { + border: 1px solid #141414; +} +#CleanupSettingsHeadLabel, +#LoadLevelHeadLabel, +#PsdSettingsHeadLabel, +#PsdSettingsGroupBox::title, +#FxSettingsPreviewShowLabel { + color: #000040; +} +#PsdSettingsGroupBox { + border: 1px solid #000040; +} +#FileDoesNotExistLabel { + color: #800000; +} +#CleanupSettingsShowButton, +#LoadLevelShowButton, +#FxSettingsPreviewShowButton { + border-width: 2px; + padding: 0px; + margin: 0px; + border-image: url("qss/gray_128/imgs/handle_border.png") 5; + image: url("qss/gray_128/imgs/plus.png"); + image-position: center center; +} +#CleanupSettingsShowButton:checked, +#LoadLevelShowButton:checked, +#FxSettingsPreviewShowButton:checked { + image: url("qss/gray_128/imgs/minus.png"); +} +ParamsPage { + qproperty-TextColor: black; +} +/*----------- Xsheet ------------*/ +/* XSheet scrollAreas (row, column and cell) */ +#xsheetScrollArea { + border: 0px; +} +#xsheetArea, +#ScrollArea, +#FunctionSegmentViewer { + border-width: 2px; + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + margin: 0px; +} +/*XsheetColumnHeader Right-click menu*/ +#xsheetColumnAreaMenu_Preview { + background-color: #e6e678; +} +#xsheetColumnAreaMenu_Lock { + background-color: #f5f5f5; +} +#xsheetColumnAreaMenu_Camstand { + background-color: #ffa480; +} +#xsheetColumnAreaMenu_Preview:selected, +#xsheetColumnAreaMenu_Lock:selected, +#xsheetColumnAreaMenu_Camstand:selected { + background-color: #000080; +} +/* Customize QScrollBar vertical*/ +#XsheetScrollBar { + background-color: #a0a0a0; + border: 1px solid black; + /* buttons */ +} +#XsheetScrollBar:vertical { + width: 18px; + margin-left: 0px; + margin-right: 0px; + margin-top: 20px; + margin-bottom: 20px; +} +#XsheetScrollBar:horizontal { + height: 18px; + margin-left: 20px; + margin-right: 20px; + margin-top: 0px; + margin-bottom: 0px; +} +#XsheetScrollBar::handle { + border-width: 4; + image-position: center center; +} +#XsheetScrollBar::handle:vertical { + border-image: url("qss/gray_128/imgs/sb_g_vhandle.png") 4; + image: url("qss/gray_128/imgs/sb_g_vline.png"); + min-height: 40px; +} +#XsheetScrollBar::handle:horizontal { + border-image: url("qss/gray_128/imgs/sb_g_hhandle.png") 4; + image: url("qss/gray_128/imgs/sb_g_hline.png"); + min-width: 40px; +} +#XsheetScrollBar::add-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::add-line:vertical { + image: url("qss/gray_128/imgs/sb_g_downarrow.png"); + height: 20px; + subcontrol-position: bottom; +} +#XsheetScrollBar::add-line:vertical:pressed { + image: url("qss/gray_128/imgs/sb_g_downarrow_pressed.png"); +} +#XsheetScrollBar::add-line:horizontal { + image: url("qss/gray_128/imgs/sb_g_rarrow.png"); + width: 20px; + subcontrol-position: right; +} +#XsheetScrollBar::add-line:horizontal:pressed { + image: url("qss/gray_128/imgs/sb_g_rarrow_pressed.png"); +} +#XsheetScrollBar::sub-line { + subcontrol-origin: margin; +} +#XsheetScrollBar::sub-line:vertical { + image: url("qss/gray_128/imgs/sb_g_uparrow.png"); + height: 20px; + subcontrol-position: top; +} +#XsheetScrollBar::sub-line:vertical:pressed { + image: url("qss/gray_128/imgs/sb_g_uparrow_pressed.png"); +} +#XsheetScrollBar::sub-line:horizontal { + image: url("qss/gray_128/imgs/sb_g_larrow.png"); + width: 20px; + subcontrol-position: left; +} +#XsheetScrollBar::sub-line:horizontal:pressed { + image: url("qss/gray_128/imgs/sb_g_larrow_pressed.png"); +} +#XsheetScrollBar::add-page { + background: none; +} +XsheetViewer { + qproperty-TextColor: black; + qproperty-BGColor: #a4a4a4; + qproperty-LightLineColor: #929092; + qproperty-MarkerLineColor: #00fff6; + qproperty-PreviewFrameTextColor: #0000ff; + qproperty-CurrentRowBgColor: #d2d2d2; + qproperty-EmptyColumnHeadColor: #c8c8c8; + qproperty-SelectedColumnTextColor: #e66464; + qproperty-EmptyCellColor: #7c7c7c; + qproperty-NotEmptyColumnColor: #a4a4a4; + qproperty-SelectedEmptyCellColor: #d2d2d2; + qproperty-LevelColumnColor: #7fdb7f; + qproperty-LevelColumnBorderColor: #2f522f; + qproperty-SelectedLevelColumnColor: #bfedbf; + qproperty-VectorColumnColor: #d4d485; + qproperty-VectorColumnBorderColor: #4f4f31; + qproperty-SelectedVectorColumnColor: #eaeac2; + qproperty-ChildColumnColor: #d69adb; + qproperty-ChildColumnBorderColor: #503952; + qproperty-SelectedChildColumnColor: #ebcded; + qproperty-FullcolorColumnColor: #9ad6db; + qproperty-FullcolorColumnBorderColor: #395052; + qproperty-SelectedFullcolorColumnColor: #cdebed; + qproperty-FxColumnColor: #82815d; + qproperty-FxColumnBorderColor: #303023; + qproperty-SelectedFxColumnColor: #c1c0ae; + qproperty-ReferenceColumnColor: #ababab; + qproperty-ReferenceColumnBorderColor: #3e3e3e; + qproperty-SelectedReferenceColumnColor: #d5d5d5; + qproperty-PaletteColumnColor: #2aab9a; + qproperty-PaletteColumnBorderColor: #0f3e38; + qproperty-SelectedPaletteColumnColor: #92ddca; + qproperty-ColumnHeadPastelizer: #ffffff; + qproperty-SelectedColumnHead: #bed2f0; + qproperty-LightLightBGColor: #fafafa; + qproperty-LightBGColor: #f0f0f0; + qproperty-DarkBGColor: #e1e1e1; + qproperty-DarkLineColor: #969696; +} +/*------- Schematic ---------*/ +#SchematicBottomFrame { + margin: 0px; + padding: 0px; + background-color: #808080; + border-style: outset; + border-left-color: #e6e6e6; + border-top-color: #e6e6e6; + border-right-color: #404040; + border-bottom-color: #404040; + border-width: 1px; + border-image: none; +} +#SchematicSceneViewer { + background-color: #373737; +} +/*------ Function Editor ---------*/ +#FunctionParametersPanel { + border: 1px solid #141414; +} +#FunctionEditorTree::branch:adjoins-item, +#ShortcutTree::branch:adjoins-item { + border-image: url("qss/gray_128/imgs/tree17_branch-end.png") 0; +} +#FunctionEditorTree::branch:has-siblings, +#ShortcutTree::branch:has-siblings { + border-image: url("qss/gray_128/imgs/tree17_vline.png") 0; +} +#FunctionEditorTree::branch:has-siblings:adjoins-item, +#ShortcutTree::branch:has-siblings:adjoins-item { + border-image: url("qss/gray_128/imgs/tree17_branch-more.png") 0; +} +#FunctionEditorTree::branch:has-children:closed, +#ShortcutTree::branch:has-children:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-closed_nosib.png"); +} +#FunctionEditorTree::branch:has-children:open, +#ShortcutTree::branch:has-children:open { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-open_nosib.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:closed, +#ShortcutTree::branch:has-children:has-siblings:closed { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-closed.png"); +} +#FunctionEditorTree::branch:has-children:has-siblings:open, +#ShortcutTree::branch:has-children:has-siblings:open { + border-image: none; + image: url("qss/gray_128/imgs/tree17_branch-open.png"); +} +FunctionPanel { + qproperty-BGColor: #e1e1e1; + qproperty-ValueLineColor: #bababa; + qproperty-FrameLineColor: #d2d2d2; + qproperty-OtherCurvesColor: #969696; + qproperty-RulerBackground: #ffffff; + qproperty-TextColor: black; + qproperty-SubColor: white; + qproperty-SelectedColor: blue; +} +FunctionTreeView { + qproperty-TextColor: black; + qproperty-CurrentTextColor: red; +} +SpreadsheetViewer { + qproperty-LightLightBGColor: #7c7c7c; + qproperty-CurrentRowBgColor: #d2d2d2; + qproperty-LightLineColor: #929092; + qproperty-MarkerLineColor: #00fff6; + qproperty-BGColor: #a4a4a4; + qproperty-VerticalLineColor: #000000; + qproperty-KeyFrameColor: #db8b36; + qproperty-KeyFrameBorderColor: #523314; + qproperty-SelectedKeyFrameColor: #edc59b; + qproperty-InBetweenColor: #c2c2b0; + qproperty-InBetweenBorderColor: #484841; + qproperty-SelectedInBetweenColor: #e1e1d8; + qproperty-SelectedEmptyColor: #bebebe; + qproperty-SelectedSceneRangeEmptyColor: #d2d2d2; + qproperty-TextColor: black; + qproperty-ColumnHeaderBorderColor: #2e2f2e; + qproperty-SelectedColumnTextColor: #ff0000; +} +#keyFrameNavigator { + border: 0px; +} +#ExpressionField { + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + background-color: white; + border-width: 2px; + margin: 0px; +} +#FunctionSegmentViewerLinkButton { + border: 2px; + margin: 0px; + image: url("qss/gray_128/imgs/segment_unlinked.png"); + background-color: #b3b3b3; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #737373; + border-bottom-color: #737373; +} +#FunctionSegmentViewerLinkButton:checked { + image: url("qss/gray_128/imgs/segment_linked.png"); + background-color: #b3b3b3; + border-style: inset; + border-left-color: #737373; + border-top-color: #737373; + border-right-color: #ffffff; + border-bottom-color: #ffffff; +} +#FunctionSegmentViewerLinkButton:disabled { + image: url("qss/gray_128/imgs/segment_disabled.png"); + background-color: #9a9a9a; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #5a5a5a; + border-bottom-color: #5a5a5a; + border: 1px; +} +/*------ Tasks Viewer ------*/ +#TaskSheetItem, +#tasksRemoveBox, +#tasksAddBox { + background-color: #9a9a9a; + border-style: inset; + border-left-color: #5a5a5a; + border-top-color: #5a5a5a; + border-right-color: #ffffff; + border-bottom-color: #ffffff; + border-width: 1px; + padding: 3px; +} +#TaskSheetItemLabel { + color: #000040; +} +/*------ Cleanup Settings------*/ +/* FileField etc. */ +#PushButton_NoPadding { + padding-left: 3px; + padding-right: 3px; + padding-top: 3px; + padding-bottom: 3px; +} +#CameraSettingsButton { + padding: 2px; + border: 0px; +} +#CameraSettingsRadioButton::indicator { + width: 21px; + height: 21px; +} +#CameraSettingsRadioButton::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock.png"); +} +#CameraSettingsRadioButton::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover.png"); +} +#CameraSettingsDPI { + color: #004000; +} +#CameraSettingsRadioButton_Small { + padding: 2px; +} +#CameraSettingsRadioButton_Small::indicator { + width: 11px; + height: 21px; +} +#CameraSettingsRadioButton_Small::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock_small.png"); +} +#CameraSettingsRadioButton_Small::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover_small.png"); +} +#ForceSquaredPixelButton { + border: none; + border-radius: 0px; + padding: 0px; + image: url("qss/gray_128/imgs/fsp_released.png"); +} +#ForceSquaredPixelButton:hover { + image: url("qss/gray_128/imgs/fsp_hover.png"); +} +#ForceSquaredPixelButton:checked { + image: url("qss/gray_128/imgs/fsp_pressed.png"); +} +/*------ Tool Options Bar------*/ +#EditToolLockButton { + spacing: 0px; + /*space between button and text*/ +} +#EditToolLockButton::indicator { + border-width: 0px; + width: 21px; + height: 21px; +} +#EditToolLockButton::indicator:unchecked { + image: url("qss/gray_128/imgs/cam_unlock.png"); +} +#EditToolLockButton::indicator:unchecked:hover { + image: url("qss/gray_128/imgs/cam_unlock_hover.png"); +} +#EditToolLockButton::indicator:checked { + image: url("qss/gray_128/imgs/cam_lock.png"); +} +#EditToolLockButton::indicator:checked:hover { + image: url("qss/gray_128/imgs/cam_lock_hover.png"); +} +/*------ Topbar and Menubar of the MainWindow ------*/ +#TopBar { + height: 22px; + background-color: #c0c0c0; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTabContainer { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#TopBarTab { + border-image: url("qss/gray_128/imgs/topbar_bg.png") 0 0 0 0 stretch stretch; + /*background: qlineargradient(x1: 0,y1: 0, x2: 1, y2: 0, stop: 0 #a0a0a0, stop: 0.5 #404040);*/ + border: 0px; + padding: 0px; +} +#TopBarTab::tab { + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 1px; + padding-left: 8px; + padding-right: 8px; + padding-top: 1px; + padding-bottom: 1px; + border: 1px solid black; + background-color: #a0a0a0; +} +#TopBarTab::tab:selected { + background-color: #cddcc0; +} +#TopBarTab::tab:hover { + background-color: #c0c0c0; +} +#StackedMenuBar { + background: #a0a0a0; + margin: 0px; + border: 0px; + padding: 0px; +} +#DockSeparator { + background-color: #9a9a9a; + border-style: outset; + border-left-color: #ffffff; + border-top-color: #ffffff; + border-right-color: #5a5a5a; + border-bottom-color: #5a5a5a; + border-width: 1; +} +/*------ Popups -------*/ +QDialog #dialogButtonFrame { + background-color: #676767; +} +#SceneSettings QLabel { + color: #000040; +} +#PreferencesPopup QListWidget { + background-color: #b3b3b3; + border-style: inset; + border-left-color: #737373; + border-top-color: #737373; + border-right-color: #ffffff; + border-bottom-color: #ffffff; + border-width: 2px; + alternate-background-color: #aaaaaa; + font-size: 14px; +} +#PreferencesPopup QListWidget::item { + padding: 3px; +} +#PreferencesPopup QListWidget::item:selected { + background-color: #000080; + color: white; +} +#PreferencesPopup QListWidget::item:hover { + background-color: #d2d2d2; +} +#OutputSettingsBox { + border: 1px solid #000040; +} +#OutputSettingsLabel { + color: #000040; +} +#OutputSettingsCameraBox { + background-color: #808080; + border-style: inset; + border-left-color: #404040; + border-top-color: #404040; + border-right-color: #e6e6e6; + border-bottom-color: #e6e6e6; + border-width: 2px; +} +#OutputSettingsShowButton { + border: 2px; + padding: 0px; + border-image: url("qss/gray_128/imgs/handle_border.png") 5; + image: url("qss/gray_128/imgs/plus.png"); + image-position: center center; +} +#OutputSettingsShowButton:checked { + image: url("qss/gray_128/imgs/minus.png"); +} +#IntPairField, +#DoublePairField { + qproperty-LightLineColor: white; + qproperty-DarkLineColor: #808080; + qproperty-HandleLeftPixmap: url("qss/gray_128/imgs/h_slider_left.png"); + qproperty-HandleRightPixmap: url("qss/gray_128/imgs/h_slider_right.png"); + qproperty-HandleLeftGrayPixmap: url("qss/gray_128/imgs/h_slider_left_disabled.png"); + qproperty-HandleRightGrayPixmap: url("qss/gray_128/imgs/h_slider_right_disabled.png"); +} +#FxSettingsLabel { + color: #004000; +} +#FxSettings { + border-width: 0px; + border-bottom: 3px double #404040; +} +#FxSettingsHelpButton { + background-color: #c0c0c0; + color: #000040; +} +#FxSettingsHelpButton:hover { + background-color: #dcdcdc; +} + +//# sourceMappingURL=gray_128.qss.map \ No newline at end of file diff --git a/stuff/config/qss/gray_128/imgs/bottomseparator.png b/stuff/config/qss/gray_128/imgs/bottomseparator.png new file mode 100644 index 0000000..e59114f Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/bottomseparator.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_lock.png b/stuff/config/qss/gray_128/imgs/cam_lock.png new file mode 100644 index 0000000..c90cb0d Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_lock.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_lock_hover.png b/stuff/config/qss/gray_128/imgs/cam_lock_hover.png new file mode 100644 index 0000000..cf1d4ad Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_lock_hover.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_lock_hover_small.png b/stuff/config/qss/gray_128/imgs/cam_lock_hover_small.png new file mode 100644 index 0000000..fdff819 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_lock_hover_small.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_lock_small.png b/stuff/config/qss/gray_128/imgs/cam_lock_small.png new file mode 100644 index 0000000..1520614 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_lock_small.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_unlock.png b/stuff/config/qss/gray_128/imgs/cam_unlock.png new file mode 100644 index 0000000..7dba22e Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_unlock.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_unlock_hover.png b/stuff/config/qss/gray_128/imgs/cam_unlock_hover.png new file mode 100644 index 0000000..ef39899 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_unlock_hover.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_unlock_hover_small.png b/stuff/config/qss/gray_128/imgs/cam_unlock_hover_small.png new file mode 100644 index 0000000..9eccd59 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_unlock_hover_small.png differ diff --git a/stuff/config/qss/gray_128/imgs/cam_unlock_small.png b/stuff/config/qss/gray_128/imgs/cam_unlock_small.png new file mode 100644 index 0000000..a5a5c7d Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/cam_unlock_small.png differ diff --git a/stuff/config/qss/gray_128/imgs/click.png b/stuff/config/qss/gray_128/imgs/click.png new file mode 100644 index 0000000..32b34a3 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/click.png differ diff --git a/stuff/config/qss/gray_128/imgs/click_pink.png b/stuff/config/qss/gray_128/imgs/click_pink.png new file mode 100644 index 0000000..0ebbd37 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/click_pink.png differ diff --git a/stuff/config/qss/gray_128/imgs/colorslider_add.png b/stuff/config/qss/gray_128/imgs/colorslider_add.png new file mode 100644 index 0000000..77029d9 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/colorslider_add.png differ diff --git a/stuff/config/qss/gray_128/imgs/colorslider_add_pressed.png b/stuff/config/qss/gray_128/imgs/colorslider_add_pressed.png new file mode 100644 index 0000000..ce0ebeb Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/colorslider_add_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/colorslider_button_bg.png b/stuff/config/qss/gray_128/imgs/colorslider_button_bg.png new file mode 100644 index 0000000..7138969 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/colorslider_button_bg.png differ diff --git a/stuff/config/qss/gray_128/imgs/colorslider_sub.png b/stuff/config/qss/gray_128/imgs/colorslider_sub.png new file mode 100644 index 0000000..f3e3e69 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/colorslider_sub.png differ diff --git a/stuff/config/qss/gray_128/imgs/colorslider_sub_pressed.png b/stuff/config/qss/gray_128/imgs/colorslider_sub_pressed.png new file mode 100644 index 0000000..374c8b0 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/colorslider_sub_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/combo_down_arrow.png b/stuff/config/qss/gray_128/imgs/combo_down_arrow.png new file mode 100644 index 0000000..3551eeb Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/combo_down_arrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/down_arrow_black.png b/stuff/config/qss/gray_128/imgs/down_arrow_black.png new file mode 100644 index 0000000..d9fbc98 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/down_arrow_black.png differ diff --git a/stuff/config/qss/gray_128/imgs/flipmarker.png b/stuff/config/qss/gray_128/imgs/flipmarker.png new file mode 100644 index 0000000..40d1563 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/flipmarker.png differ diff --git a/stuff/config/qss/gray_128/imgs/flipslider.png b/stuff/config/qss/gray_128/imgs/flipslider.png new file mode 100644 index 0000000..829a740 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/flipslider.png differ diff --git a/stuff/config/qss/gray_128/imgs/fpssb_g_larrow.png b/stuff/config/qss/gray_128/imgs/fpssb_g_larrow.png new file mode 100644 index 0000000..d0823a6 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fpssb_g_larrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/fpssb_g_larrow_pressed.png b/stuff/config/qss/gray_128/imgs/fpssb_g_larrow_pressed.png new file mode 100644 index 0000000..9655d59 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fpssb_g_larrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow.png b/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow.png new file mode 100644 index 0000000..babfc3e Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow_pressed.png b/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow_pressed.png new file mode 100644 index 0000000..dd3f8bb Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fpssb_g_rarrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/fsp_hover.png b/stuff/config/qss/gray_128/imgs/fsp_hover.png new file mode 100644 index 0000000..5d755d5 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fsp_hover.png differ diff --git a/stuff/config/qss/gray_128/imgs/fsp_pressed.png b/stuff/config/qss/gray_128/imgs/fsp_pressed.png new file mode 100644 index 0000000..f8a85f8 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fsp_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/fsp_released.png b/stuff/config/qss/gray_128/imgs/fsp_released.png new file mode 100644 index 0000000..bf5ecfc Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/fsp_released.png differ diff --git a/stuff/config/qss/gray_128/imgs/h_slider_left.png b/stuff/config/qss/gray_128/imgs/h_slider_left.png new file mode 100644 index 0000000..4c67ce6 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/h_slider_left.png differ diff --git a/stuff/config/qss/gray_128/imgs/h_slider_left_disabled.png b/stuff/config/qss/gray_128/imgs/h_slider_left_disabled.png new file mode 100644 index 0000000..6657149 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/h_slider_left_disabled.png differ diff --git a/stuff/config/qss/gray_128/imgs/h_slider_right.png b/stuff/config/qss/gray_128/imgs/h_slider_right.png new file mode 100644 index 0000000..7ac913e Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/h_slider_right.png differ diff --git a/stuff/config/qss/gray_128/imgs/h_slider_right_disabled.png b/stuff/config/qss/gray_128/imgs/h_slider_right_disabled.png new file mode 100644 index 0000000..b08f75e Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/h_slider_right_disabled.png differ diff --git a/stuff/config/qss/gray_128/imgs/handle_border.png b/stuff/config/qss/gray_128/imgs/handle_border.png new file mode 100644 index 0000000..5d2c5a0 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/handle_border.png differ diff --git a/stuff/config/qss/gray_128/imgs/left_arrow_black.png b/stuff/config/qss/gray_128/imgs/left_arrow_black.png new file mode 100644 index 0000000..b89cdcc Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/left_arrow_black.png differ diff --git a/stuff/config/qss/gray_128/imgs/minus.png b/stuff/config/qss/gray_128/imgs/minus.png new file mode 100644 index 0000000..b10fa0f Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/minus.png differ diff --git a/stuff/config/qss/gray_128/imgs/over.png b/stuff/config/qss/gray_128/imgs/over.png new file mode 100644 index 0000000..f44d166 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/over.png differ diff --git a/stuff/config/qss/gray_128/imgs/over_pressed_yellow.png b/stuff/config/qss/gray_128/imgs/over_pressed_yellow.png new file mode 100644 index 0000000..892023a Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/over_pressed_yellow.png differ diff --git a/stuff/config/qss/gray_128/imgs/over_yellow.png b/stuff/config/qss/gray_128/imgs/over_yellow.png new file mode 100644 index 0000000..fc182a6 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/over_yellow.png differ diff --git a/stuff/config/qss/gray_128/imgs/plus.png b/stuff/config/qss/gray_128/imgs/plus.png new file mode 100644 index 0000000..81ed056 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/plus.png differ diff --git a/stuff/config/qss/gray_128/imgs/right_arrow_black.png b/stuff/config/qss/gray_128/imgs/right_arrow_black.png new file mode 100644 index 0000000..8878c62 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/right_arrow_black.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_downarrow.png b/stuff/config/qss/gray_128/imgs/sb_g_downarrow.png new file mode 100644 index 0000000..f5e678c Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_downarrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_downarrow_pressed.png b/stuff/config/qss/gray_128/imgs/sb_g_downarrow_pressed.png new file mode 100644 index 0000000..ca150f6 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_downarrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_hhandle.png b/stuff/config/qss/gray_128/imgs/sb_g_hhandle.png new file mode 100644 index 0000000..58d0d27 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_hhandle.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_hline.png b/stuff/config/qss/gray_128/imgs/sb_g_hline.png new file mode 100644 index 0000000..6d6f343 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_hline.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_larrow.png b/stuff/config/qss/gray_128/imgs/sb_g_larrow.png new file mode 100644 index 0000000..db68d6d Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_larrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_larrow_pressed.png b/stuff/config/qss/gray_128/imgs/sb_g_larrow_pressed.png new file mode 100644 index 0000000..23acd01 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_larrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_rarrow.png b/stuff/config/qss/gray_128/imgs/sb_g_rarrow.png new file mode 100644 index 0000000..17c8865 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_rarrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_rarrow_pressed.png b/stuff/config/qss/gray_128/imgs/sb_g_rarrow_pressed.png new file mode 100644 index 0000000..873e91b Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_rarrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_uparrow.png b/stuff/config/qss/gray_128/imgs/sb_g_uparrow.png new file mode 100644 index 0000000..3fd43db Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_uparrow.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_uparrow_pressed.png b/stuff/config/qss/gray_128/imgs/sb_g_uparrow_pressed.png new file mode 100644 index 0000000..37b4e73 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_uparrow_pressed.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_vhandle.png b/stuff/config/qss/gray_128/imgs/sb_g_vhandle.png new file mode 100644 index 0000000..1683aba Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_vhandle.png differ diff --git a/stuff/config/qss/gray_128/imgs/sb_g_vline.png b/stuff/config/qss/gray_128/imgs/sb_g_vline.png new file mode 100644 index 0000000..fc1e954 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/sb_g_vline.png differ diff --git a/stuff/config/qss/gray_128/imgs/segment_disabled.png b/stuff/config/qss/gray_128/imgs/segment_disabled.png new file mode 100644 index 0000000..48a6a15 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/segment_disabled.png differ diff --git a/stuff/config/qss/gray_128/imgs/segment_linked.png b/stuff/config/qss/gray_128/imgs/segment_linked.png new file mode 100644 index 0000000..136d148 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/segment_linked.png differ diff --git a/stuff/config/qss/gray_128/imgs/segment_unlinked.png b/stuff/config/qss/gray_128/imgs/segment_unlinked.png new file mode 100644 index 0000000..25e9c63 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/segment_unlinked.png differ diff --git a/stuff/config/qss/gray_128/imgs/separator.png b/stuff/config/qss/gray_128/imgs/separator.png new file mode 100644 index 0000000..6be22af Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/separator.png differ diff --git a/stuff/config/qss/gray_128/imgs/separator_h.png b/stuff/config/qss/gray_128/imgs/separator_h.png new file mode 100644 index 0000000..72cc296 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/separator_h.png differ diff --git a/stuff/config/qss/gray_128/imgs/separator_v.png b/stuff/config/qss/gray_128/imgs/separator_v.png new file mode 100644 index 0000000..7e9b0ba Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/separator_v.png differ diff --git a/stuff/config/qss/gray_128/imgs/topbar_bg.png b/stuff/config/qss/gray_128/imgs/topbar_bg.png new file mode 100644 index 0000000..60fc056 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/topbar_bg.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-closed.png b/stuff/config/qss/gray_128/imgs/tree17_branch-closed.png new file mode 100644 index 0000000..bac628f Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-closed.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-closed_nosib.png b/stuff/config/qss/gray_128/imgs/tree17_branch-closed_nosib.png new file mode 100644 index 0000000..259c02c Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-closed_nosib.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-end.png b/stuff/config/qss/gray_128/imgs/tree17_branch-end.png new file mode 100644 index 0000000..72aed6d Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-end.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-more.png b/stuff/config/qss/gray_128/imgs/tree17_branch-more.png new file mode 100644 index 0000000..43a7581 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-more.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-open.png b/stuff/config/qss/gray_128/imgs/tree17_branch-open.png new file mode 100644 index 0000000..468e2a8 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-open.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_branch-open_nosib.png b/stuff/config/qss/gray_128/imgs/tree17_branch-open_nosib.png new file mode 100644 index 0000000..dbe49d2 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_branch-open_nosib.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree17_vline.png b/stuff/config/qss/gray_128/imgs/tree17_vline.png new file mode 100644 index 0000000..f28e715 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree17_vline.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-closed.png b/stuff/config/qss/gray_128/imgs/tree_branch-closed.png new file mode 100644 index 0000000..b9f4bdc Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-closed.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-closed_nosib.png b/stuff/config/qss/gray_128/imgs/tree_branch-closed_nosib.png new file mode 100644 index 0000000..fdf53f6 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-closed_nosib.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-end.png b/stuff/config/qss/gray_128/imgs/tree_branch-end.png new file mode 100644 index 0000000..81a39fa Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-end.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-more.png b/stuff/config/qss/gray_128/imgs/tree_branch-more.png new file mode 100644 index 0000000..88648eb Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-more.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-open.png b/stuff/config/qss/gray_128/imgs/tree_branch-open.png new file mode 100644 index 0000000..4740fc4 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-open.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_branch-open_nosib.png b/stuff/config/qss/gray_128/imgs/tree_branch-open_nosib.png new file mode 100644 index 0000000..6eef6fa Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_branch-open_nosib.png differ diff --git a/stuff/config/qss/gray_128/imgs/tree_vline.png b/stuff/config/qss/gray_128/imgs/tree_vline.png new file mode 100644 index 0000000..f379a2b Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/tree_vline.png differ diff --git a/stuff/config/qss/gray_128/imgs/up_arrow_black.png b/stuff/config/qss/gray_128/imgs/up_arrow_black.png new file mode 100644 index 0000000..6c27909 Binary files /dev/null and b/stuff/config/qss/gray_128/imgs/up_arrow_black.png differ diff --git a/stuff/config/qss/production/Thumbs.db b/stuff/config/qss/production/Thumbs.db new file mode 100644 index 0000000..ef05441 Binary files /dev/null and b/stuff/config/qss/production/Thumbs.db differ diff --git a/stuff/config/qss/production/bottomseparator.png b/stuff/config/qss/production/bottomseparator.png new file mode 100644 index 0000000..2461174 Binary files /dev/null and b/stuff/config/qss/production/bottomseparator.png differ diff --git a/stuff/config/qss/production/but.png b/stuff/config/qss/production/but.png new file mode 100644 index 0000000..ad31be9 Binary files /dev/null and b/stuff/config/qss/production/but.png differ diff --git a/stuff/config/qss/production/butdis.png b/stuff/config/qss/production/butdis.png new file mode 100644 index 0000000..9f3ed2c Binary files /dev/null and b/stuff/config/qss/production/butdis.png differ diff --git a/stuff/config/qss/production/buthover.png b/stuff/config/qss/production/buthover.png new file mode 100644 index 0000000..9f3ed2c Binary files /dev/null and b/stuff/config/qss/production/buthover.png differ diff --git a/stuff/config/qss/production/chk_chk.png b/stuff/config/qss/production/chk_chk.png new file mode 100644 index 0000000..b7723b7 Binary files /dev/null and b/stuff/config/qss/production/chk_chk.png differ diff --git a/stuff/config/qss/production/chk_unchk.png b/stuff/config/qss/production/chk_unchk.png new file mode 100644 index 0000000..46f9a4d Binary files /dev/null and b/stuff/config/qss/production/chk_unchk.png differ diff --git a/stuff/config/qss/production/click.png b/stuff/config/qss/production/click.png new file mode 100644 index 0000000..c8a1fe9 Binary files /dev/null and b/stuff/config/qss/production/click.png differ diff --git a/stuff/config/qss/production/current.png b/stuff/config/qss/production/current.png new file mode 100644 index 0000000..7559bd5 Binary files /dev/null and b/stuff/config/qss/production/current.png differ diff --git a/stuff/config/qss/production/down_arrow.png b/stuff/config/qss/production/down_arrow.png new file mode 100644 index 0000000..31da5d7 Binary files /dev/null and b/stuff/config/qss/production/down_arrow.png differ diff --git a/stuff/config/qss/production/down_arrow_black.png b/stuff/config/qss/production/down_arrow_black.png new file mode 100644 index 0000000..d9fbc98 Binary files /dev/null and b/stuff/config/qss/production/down_arrow_black.png differ diff --git a/stuff/config/qss/production/down_arrow_disabled.png b/stuff/config/qss/production/down_arrow_disabled.png new file mode 100644 index 0000000..98c55c3 Binary files /dev/null and b/stuff/config/qss/production/down_arrow_disabled.png differ diff --git a/stuff/config/qss/production/flipmarker.png b/stuff/config/qss/production/flipmarker.png new file mode 100644 index 0000000..7e0c0de Binary files /dev/null and b/stuff/config/qss/production/flipmarker.png differ diff --git a/stuff/config/qss/production/flipslider.png b/stuff/config/qss/production/flipslider.png new file mode 100644 index 0000000..cfa191e Binary files /dev/null and b/stuff/config/qss/production/flipslider.png differ diff --git a/stuff/config/qss/production/frame.png b/stuff/config/qss/production/frame.png new file mode 100644 index 0000000..a3da918 Binary files /dev/null and b/stuff/config/qss/production/frame.png differ diff --git a/stuff/config/qss/production/framedisable.png b/stuff/config/qss/production/framedisable.png new file mode 100644 index 0000000..1b897bc Binary files /dev/null and b/stuff/config/qss/production/framedisable.png differ diff --git a/stuff/config/qss/production/framehover.png b/stuff/config/qss/production/framehover.png new file mode 100644 index 0000000..1c14fec Binary files /dev/null and b/stuff/config/qss/production/framehover.png differ diff --git a/stuff/config/qss/production/ftab.png b/stuff/config/qss/production/ftab.png new file mode 100644 index 0000000..155d4a8 Binary files /dev/null and b/stuff/config/qss/production/ftab.png differ diff --git a/stuff/config/qss/production/ftbtab.png b/stuff/config/qss/production/ftbtab.png new file mode 100644 index 0000000..834b0f7 Binary files /dev/null and b/stuff/config/qss/production/ftbtab.png differ diff --git a/stuff/config/qss/production/fuptab.png b/stuff/config/qss/production/fuptab.png new file mode 100644 index 0000000..aefec60 Binary files /dev/null and b/stuff/config/qss/production/fuptab.png differ diff --git a/stuff/config/qss/production/fuptbtab.png b/stuff/config/qss/production/fuptbtab.png new file mode 100644 index 0000000..0e3b303 Binary files /dev/null and b/stuff/config/qss/production/fuptbtab.png differ diff --git a/stuff/config/qss/production/h_chandle.png b/stuff/config/qss/production/h_chandle.png new file mode 100644 index 0000000..59cbcb2 Binary files /dev/null and b/stuff/config/qss/production/h_chandle.png differ diff --git a/stuff/config/qss/production/h_handle.png b/stuff/config/qss/production/h_handle.png new file mode 100644 index 0000000..995138c Binary files /dev/null and b/stuff/config/qss/production/h_handle.png differ diff --git a/stuff/config/qss/production/h_slider_left.png b/stuff/config/qss/production/h_slider_left.png new file mode 100644 index 0000000..a7bc1ce Binary files /dev/null and b/stuff/config/qss/production/h_slider_left.png differ diff --git a/stuff/config/qss/production/h_slider_right.png b/stuff/config/qss/production/h_slider_right.png new file mode 100644 index 0000000..a05ba8f Binary files /dev/null and b/stuff/config/qss/production/h_slider_right.png differ diff --git a/stuff/config/qss/production/hsplitter_handle.png b/stuff/config/qss/production/hsplitter_handle.png new file mode 100644 index 0000000..ad93550 Binary files /dev/null and b/stuff/config/qss/production/hsplitter_handle.png differ diff --git a/stuff/config/qss/production/left_arrow_black.png b/stuff/config/qss/production/left_arrow_black.png new file mode 100644 index 0000000..b89cdcc Binary files /dev/null and b/stuff/config/qss/production/left_arrow_black.png differ diff --git a/stuff/config/qss/production/ltab.png b/stuff/config/qss/production/ltab.png new file mode 100644 index 0000000..7fe6408 Binary files /dev/null and b/stuff/config/qss/production/ltab.png differ diff --git a/stuff/config/qss/production/ltbtab.png b/stuff/config/qss/production/ltbtab.png new file mode 100644 index 0000000..834b0f7 Binary files /dev/null and b/stuff/config/qss/production/ltbtab.png differ diff --git a/stuff/config/qss/production/menuseparator.PNG b/stuff/config/qss/production/menuseparator.PNG new file mode 100644 index 0000000..0a44fae Binary files /dev/null and b/stuff/config/qss/production/menuseparator.PNG differ diff --git a/stuff/config/qss/production/oouptab.png b/stuff/config/qss/production/oouptab.png new file mode 100644 index 0000000..6faf3fb Binary files /dev/null and b/stuff/config/qss/production/oouptab.png differ diff --git a/stuff/config/qss/production/out_sb_h.png b/stuff/config/qss/production/out_sb_h.png new file mode 100644 index 0000000..8ea48c5 Binary files /dev/null and b/stuff/config/qss/production/out_sb_h.png differ diff --git a/stuff/config/qss/production/over.png b/stuff/config/qss/production/over.png new file mode 100644 index 0000000..7525799 Binary files /dev/null and b/stuff/config/qss/production/over.png differ diff --git a/stuff/config/qss/production/production.qss b/stuff/config/qss/production/production.qss new file mode 100644 index 0000000..e8b8e45 --- /dev/null +++ b/stuff/config/qss/production/production.qss @@ -0,0 +1,963 @@ +/* TOONZ 6.2 (SP1) Qt Style Test + Digital Video 2011 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(60,60,60); + background-color: rgb(60,60,60); +} +/*---------------------------------------------------------------------------*/ +TPanel { + qproperty-BGColor: rgb(140,140,140); + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-image: url(qss/production/topfade.png) 10; + background-color: rgb(180,180,180); +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(140,140,140); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: grey; + background-color: rgb(140,140,140); +} +QMenu { + margin: 0px; + background-color: rgb(140,140,140); + border: 4px solid black; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/production/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +QMenu::item:disabled { + color: rgb(90,90,90); + background-color: rgb(140,140,140); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/production/chk_unchk.png); +} +QMenu::indicator:checked { + image: url(qss/production/chk_chk.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + margin:2px; + background-color: rgb(140,140,140); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background: rgb(140,140,140); +} +/*Text disable color*/ +QFrame:disabled { + color: rgb(140,140,140); +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(90,90,90); + background-color: rgb(115,115,115); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(115,115,115); +} +QDialog #dialogMainFrame { + background-color: rgb(140,140,140); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(90,90,90); +} +StyleEditor QToolBar { + border-right: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background: rgb(100,100,100); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(145,145,145); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background: rgb(120,120,120); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(140,140,140); +} +#dvTopBar { + background-color: rgb(115,115,115); +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SpreadsheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135 ); + qproperty-DarkLineColor: rgb(90,90,90); +} +#ScrollArea { + border:1px solid rgb(95,95,95); +} +#CellScrollArea { + border-top:1px solid rgb(95,95,95); + border-left:1px solid rgb(95,95,95); +} +XsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135); + qproperty-DarkLineColor: rgb(90,90,90); +} +#xsheetScrollArea { + background: rgb(140,140,140); + margin: 1px; +} +#cornerWidget { + background: rgb(140,140,140); +} +#XshColumnChangeObjectWidget { + background-color: rgb(155,155,155); + selection-background-color: rgb(49,106,197); + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(140,140,140); + qproperty-ValueLineColor: rgb(160,160,160); + qproperty-FrameLineColor: rgb(150,150,150); + qproperty-OtherCurvesColor: rgb(95,95,95); + qproperty-RulerBackground: rgb(160,160,160); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(140,140,140); + qproperty-LightLineColor: rgb(100,100,100); + qproperty-DarkLineColor: rgb(95,95,95); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(90,90,90); + background-color: rgb(140,140,140); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/production/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(140,140,140); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar */ +#toolBar { + margin: 1px; + background-color: rgb(140,140,140); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(90,90,90); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(140,140,140); +} +#toolBar QToolButton:pressed { + border-image: url(qss/production/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/production/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(140,140,140); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(90,90,90); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(140,140,140); + border: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/production/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/production/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/production/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/production/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + border-image: url(qss/production/but.png) 2; + border-width: 2; + color: black; +} +/*---------------------------------------------------------------------------*/ +QComboBox[editable="false"] { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/production/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(90,90,90); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/production/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; +} +QComboBox[editable="false"]::drop-down { + width: 20px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(160,160,160); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(180,180,180); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +#ScrollLeftButton { + image: url(qss/production/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/production/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/production/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/production/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/*---------------------------------------------------------------------------*/ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QToolButton:disabled { + border-image: url(qss/production/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(145,145,145); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/production/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/production/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + border-image: url(qss/production/frame.png) 3; + border-width: 3; +} +QLineEdit::focus { + border-image: url(qss/production/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(40,40,40); + border-image: url(qss/production/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit */ +QPlainTextEdit, QTexEdit { + border-image: url(qss/production/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/production/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit:disabled { + color: rgb(90,90,90); + border-image: url(qss/production/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; +} +QGroupBox:enabled +{ + border-color: rgb(90, 90, 90); +} +QGroupBox:disabled +{ + border-color: rgb(140, 140, 140); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/production/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal { + image: url(qss/production/h_handle.png); + width: 16px; + margin: -16px -4px; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/production/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/production/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightGrayPixmap: url(qss/production/h_slider_right.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/production/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightGrayPixmap: url(qss/production/h_slider_right.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + qproperty-PBHeight: 10; + + qproperty-PBOverlay: url(qss/production/flipslider.png); + qproperty-PBMarker: url(qss/production/flipmarker.png); + + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ + +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/production/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/production/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/production/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/production/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/production/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/production/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(120,120,120); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/production/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/production/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/production/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/production/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/production/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/production/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/production/sb_hhandle.png)3; + border-width: 3; + image: url(qss/production/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/production/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/production/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/production/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/production/sb_vhandle.png)3; + border-width: 3; + image: url(qss/production/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/production/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/production/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/production/chk_unchk.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/production/chk_chk.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/production/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/production/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(120,120,120); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(120,120,120); +} +QSplitter::handle:horizontal { + image: url(qss/production/hsplitter_handle.png); + border-left: 1px solid rgb(90,90,90); + border-right: 1px solid rgb(90,90,90); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/production/vsplitter_handle.png); + border-top: 1px solid rgb(90,90,90); + border-bottom: 1px solid rgb(90,90,90); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(115,115,115); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; + alternate-background-color: rgb(90, 90, 90); +} diff --git a/stuff/config/qss/production/production_mac.qss b/stuff/config/qss/production/production_mac.qss new file mode 100644 index 0000000..2397de1 --- /dev/null +++ b/stuff/config/qss/production/production_mac.qss @@ -0,0 +1,998 @@ +/* TOONZ 6.2 (SP1) Qt Style Test + Digital Video 2011 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(60,60,60); + background-color: rgb(60,60,60); +} +/*---------------------------------------------------------------------------*/ +/*------------ Mac OS ------------*/ +TPanel { + qproperty-BGColor: rgb(140,140,140); + background-color: rgb(140,140,140); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-color: rgb(180,180,180); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(140,140,140); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: gray; + background-color: rgb(140,140,140); +} +QMenu { + margin: 2px; + background-color: rgb(140,140,140); + border: 4px solid black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/production/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + color: white; +} +QMenu::item:disabled { + color: rgb(90,90,90); + background-color: rgb(140,140,140); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/production/chk_unchk.png); +} +QMenu::indicator:checked { + image: url(qss/production/chk_chk.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + margin:2px; + background-color: rgb(140,140,140); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background: rgb(140,140,140); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*Text disable color*/ +QFrame:disabled { + color: rgb(120,120,120); +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(90,90,90); + background-color: rgb(115,115,115); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(115,115,115); +} +QDialog #dialogMainFrame { + background-color: rgb(140,140,140); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(90,90,90); +} +StyleEditor QToolBar { + border-right: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/production/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background: rgb(100,100,100); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(145,145,145); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background: rgb(120,120,120); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(140,140,140); +} +#dvTopBar { + background-image: url(qss/production/topfade.png) 10; +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SpreadsheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135 ); + qproperty-DarkLineColor: rgb(90,90,90); +} +#ScrollArea { + border:1px solid rgb(95,95,95); +} +#CellScrollArea { + border-top:1px solid rgb(95,95,95); + border-left:1px solid rgb(95,95,95); +} +XsheetViewer { + background: rgb(140,140,140); + qproperty-LightLightBGColor: rgb(200,200,200); + qproperty-LightBGColor: rgb(155,155,155); + qproperty-BGColor: rgb(125,125,125); + qproperty-DarkBGColor: rgb(160,160,160); + qproperty-LightLineColor: rgb(135,135,135); + qproperty-DarkLineColor: rgb(90,90,90); +} +#xsheetScrollArea { + background: rgb(140,140,140); + margin: 1px; +} +#cornerWidget { + background: rgb(140,140,140); +} +#XshColumnChangeObjectWidget { + background-color: rgb(155,155,155); + selection-background-color: rgb(49,106,197); + border: 1px solid black +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(140,140,140); + qproperty-ValueLineColor: rgb(160,160,160); + qproperty-FrameLineColor: rgb(150,150,150); + qproperty-OtherCurvesColor: rgb(95,95,95); + qproperty-RulerBackground: rgb(160,160,160); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(140,140,140); + qproperty-LightLineColor: rgb(100,100,100); + qproperty-DarkLineColor: rgb(95,95,95); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(90,90,90); + background-color: rgb(140,140,140); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/production/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(140,140,140); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar */ +#toolBar { + margin: 1px; + background-color: rgb(140,140,140); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(90,90,90); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(140,140,140); +} +#toolBar QToolButton:pressed { + border-image: url(qss/production/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/production/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(140,140,140); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(90,90,90); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(140,140,140); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(140,140,140); + border: 1px solid rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/production/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/production/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/production/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/production/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + border-image: url(qss/production/but.png) 2; + border-width: 2; + color: black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +QComboBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QComboBox[editable="false"] { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/production/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(90,90,90); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/production/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; +} +QComboBox[editable="false"]::drop-down { + width: 15px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(160,160,160); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(180,180,180); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +#ScrollLeftButton { + image: url(qss/production/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/production/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/production/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/production/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/*---------------------------------------------------------------------------*/ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/production/but.png) 2; + border-width: 2; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QToolButton:disabled { + border-image: url(qss/production/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/production/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/production/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/production/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/production/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(145,145,145); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/production/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/production/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + border-image: url(qss/production/frame.png) 3; + border-width: 3; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QLineEdit::focus { + border-image: url(qss/production/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(40,40,40); + border-image: url(qss/production/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit */ +QPlainTextEdit, QTexEdit { + border-image: url(qss/production/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/production/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit:disabled { + color: rgb(135,135,135); + border-image: url(qss/production/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QGroupBox:enabled +{ + border-color: rgb(90, 90, 90); +} +QGroupBox:disabled +{ + border-color: rgb(140, 140, 140); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(140,140,140); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/production/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal { + image: url(qss/production/h_handle.png); + width: 16px; + margin: -16px -4px; +} +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/production/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/production/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightGrayPixmap: url(qss/production/h_slider_right.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/production/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/production/h_slider_left.png); + qproperty-HandleRightGrayPixmap: url(qss/production/h_slider_right.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + qproperty-PBHeight: 10; + + qproperty-PBOverlay: url(qss/production/flipslider.png); + qproperty-PBMarker: url(qss/production/flipmarker.png); + + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/production/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/production/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/production/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/production/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/production/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/production/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(120,120,120); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/production/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/production/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/production/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/production/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/production/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/production/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/production/sb_hhandle.png)3; + border-width: 3; + image: url(qss/production/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/production/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/production/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/production/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/production/sb_vhandle.png)3; + border-width: 3; + image: url(qss/production/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/production/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/production/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/production/chk_unchk.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/production/chk_chk.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/production/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/production/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(120,120,120); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(120,120,120); +} +QSplitter::handle:horizontal { + image: url(qss/production/hsplitter_handle.png); + border-left: 1px solid rgb(90,90,90); + border-right: 1px solid rgb(90,90,90); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/production/vsplitter_handle.png); + border-top: 1px solid rgb(90,90,90); + border-bottom: 1px solid rgb(90,90,90); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(115,115,115); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; + alternate-background-color: rgb(90, 90, 90); +} diff --git a/stuff/config/qss/production/radio.png b/stuff/config/qss/production/radio.png new file mode 100644 index 0000000..11b3d03 Binary files /dev/null and b/stuff/config/qss/production/radio.png differ diff --git a/stuff/config/qss/production/radio_p.png b/stuff/config/qss/production/radio_p.png new file mode 100644 index 0000000..f85b433 Binary files /dev/null and b/stuff/config/qss/production/radio_p.png differ diff --git a/stuff/config/qss/production/right_arrow_black.png b/stuff/config/qss/production/right_arrow_black.png new file mode 100644 index 0000000..8878c62 Binary files /dev/null and b/stuff/config/qss/production/right_arrow_black.png differ diff --git a/stuff/config/qss/production/sb_downarrow.png b/stuff/config/qss/production/sb_downarrow.png new file mode 100644 index 0000000..8590401 Binary files /dev/null and b/stuff/config/qss/production/sb_downarrow.png differ diff --git a/stuff/config/qss/production/sb_h.png b/stuff/config/qss/production/sb_h.png new file mode 100644 index 0000000..13d8d42 Binary files /dev/null and b/stuff/config/qss/production/sb_h.png differ diff --git a/stuff/config/qss/production/sb_hhandle.png b/stuff/config/qss/production/sb_hhandle.png new file mode 100644 index 0000000..8a54c49 Binary files /dev/null and b/stuff/config/qss/production/sb_hhandle.png differ diff --git a/stuff/config/qss/production/sb_hline.png b/stuff/config/qss/production/sb_hline.png new file mode 100644 index 0000000..a94e491 Binary files /dev/null and b/stuff/config/qss/production/sb_hline.png differ diff --git a/stuff/config/qss/production/sb_larrow.png b/stuff/config/qss/production/sb_larrow.png new file mode 100644 index 0000000..f7d81ed Binary files /dev/null and b/stuff/config/qss/production/sb_larrow.png differ diff --git a/stuff/config/qss/production/sb_rarrow.png b/stuff/config/qss/production/sb_rarrow.png new file mode 100644 index 0000000..b032c92 Binary files /dev/null and b/stuff/config/qss/production/sb_rarrow.png differ diff --git a/stuff/config/qss/production/sb_uparrow.png b/stuff/config/qss/production/sb_uparrow.png new file mode 100644 index 0000000..89dcd79 Binary files /dev/null and b/stuff/config/qss/production/sb_uparrow.png differ diff --git a/stuff/config/qss/production/sb_v.png b/stuff/config/qss/production/sb_v.png new file mode 100644 index 0000000..9c60cab Binary files /dev/null and b/stuff/config/qss/production/sb_v.png differ diff --git a/stuff/config/qss/production/sb_vhandle.png b/stuff/config/qss/production/sb_vhandle.png new file mode 100644 index 0000000..388d0e0 Binary files /dev/null and b/stuff/config/qss/production/sb_vhandle.png differ diff --git a/stuff/config/qss/production/sb_vline.png b/stuff/config/qss/production/sb_vline.png new file mode 100644 index 0000000..2ac4415 Binary files /dev/null and b/stuff/config/qss/production/sb_vline.png differ diff --git a/stuff/config/qss/production/sl_groove.png b/stuff/config/qss/production/sl_groove.png new file mode 100644 index 0000000..81b2865 Binary files /dev/null and b/stuff/config/qss/production/sl_groove.png differ diff --git a/stuff/config/qss/production/tab.png b/stuff/config/qss/production/tab.png new file mode 100644 index 0000000..8a437d3 Binary files /dev/null and b/stuff/config/qss/production/tab.png differ diff --git a/stuff/config/qss/production/tbtab.png b/stuff/config/qss/production/tbtab.png new file mode 100644 index 0000000..834b0f7 Binary files /dev/null and b/stuff/config/qss/production/tbtab.png differ diff --git a/stuff/config/qss/production/titlebar_horizontal.png b/stuff/config/qss/production/titlebar_horizontal.png new file mode 100644 index 0000000..962f0cc Binary files /dev/null and b/stuff/config/qss/production/titlebar_horizontal.png differ diff --git a/stuff/config/qss/production/titlebar_vertical _inactive.png b/stuff/config/qss/production/titlebar_vertical _inactive.png new file mode 100644 index 0000000..f365f9e Binary files /dev/null and b/stuff/config/qss/production/titlebar_vertical _inactive.png differ diff --git a/stuff/config/qss/production/titlebar_vertical.png b/stuff/config/qss/production/titlebar_vertical.png new file mode 100644 index 0000000..f365f9e Binary files /dev/null and b/stuff/config/qss/production/titlebar_vertical.png differ diff --git a/stuff/config/qss/production/topfade.png b/stuff/config/qss/production/topfade.png new file mode 100644 index 0000000..e021e97 Binary files /dev/null and b/stuff/config/qss/production/topfade.png differ diff --git a/stuff/config/qss/production/up_arrow.png b/stuff/config/qss/production/up_arrow.png new file mode 100644 index 0000000..749c418 Binary files /dev/null and b/stuff/config/qss/production/up_arrow.png differ diff --git a/stuff/config/qss/production/up_arrow_black.png b/stuff/config/qss/production/up_arrow_black.png new file mode 100644 index 0000000..6c27909 Binary files /dev/null and b/stuff/config/qss/production/up_arrow_black.png differ diff --git a/stuff/config/qss/production/up_arrow_disabled.png b/stuff/config/qss/production/up_arrow_disabled.png new file mode 100644 index 0000000..365da38 Binary files /dev/null and b/stuff/config/qss/production/up_arrow_disabled.png differ diff --git a/stuff/config/qss/production/uptab.png b/stuff/config/qss/production/uptab.png new file mode 100644 index 0000000..b470ef8 Binary files /dev/null and b/stuff/config/qss/production/uptab.png differ diff --git a/stuff/config/qss/production/uptbtab.png b/stuff/config/qss/production/uptbtab.png new file mode 100644 index 0000000..0e3b303 Binary files /dev/null and b/stuff/config/qss/production/uptbtab.png differ diff --git a/stuff/config/qss/production/vsplitter_handle.png b/stuff/config/qss/production/vsplitter_handle.png new file mode 100644 index 0000000..a1e7564 Binary files /dev/null and b/stuff/config/qss/production/vsplitter_handle.png differ diff --git a/stuff/config/qss/standard/bottomseparator.png b/stuff/config/qss/standard/bottomseparator.png new file mode 100644 index 0000000..2461174 Binary files /dev/null and b/stuff/config/qss/standard/bottomseparator.png differ diff --git a/stuff/config/qss/standard/but.png b/stuff/config/qss/standard/but.png new file mode 100644 index 0000000..b683f77 Binary files /dev/null and b/stuff/config/qss/standard/but.png differ diff --git a/stuff/config/qss/standard/butdis.png b/stuff/config/qss/standard/butdis.png new file mode 100644 index 0000000..1318fa8 Binary files /dev/null and b/stuff/config/qss/standard/butdis.png differ diff --git a/stuff/config/qss/standard/buthover.png b/stuff/config/qss/standard/buthover.png new file mode 100644 index 0000000..8e96909 Binary files /dev/null and b/stuff/config/qss/standard/buthover.png differ diff --git a/stuff/config/qss/standard/chk_chk.png b/stuff/config/qss/standard/chk_chk.png new file mode 100644 index 0000000..c8b9cc4 Binary files /dev/null and b/stuff/config/qss/standard/chk_chk.png differ diff --git a/stuff/config/qss/standard/chk_chk_disabled.png b/stuff/config/qss/standard/chk_chk_disabled.png new file mode 100644 index 0000000..cf08a05 Binary files /dev/null and b/stuff/config/qss/standard/chk_chk_disabled.png differ diff --git a/stuff/config/qss/standard/chk_unchk.png b/stuff/config/qss/standard/chk_unchk.png new file mode 100644 index 0000000..ab9e9b6 Binary files /dev/null and b/stuff/config/qss/standard/chk_unchk.png differ diff --git a/stuff/config/qss/standard/chk_unchk_disabled.png b/stuff/config/qss/standard/chk_unchk_disabled.png new file mode 100644 index 0000000..cfa015d Binary files /dev/null and b/stuff/config/qss/standard/chk_unchk_disabled.png differ diff --git a/stuff/config/qss/standard/click.png b/stuff/config/qss/standard/click.png new file mode 100644 index 0000000..6e42b1c Binary files /dev/null and b/stuff/config/qss/standard/click.png differ diff --git a/stuff/config/qss/standard/current.png b/stuff/config/qss/standard/current.png new file mode 100644 index 0000000..dca53d5 Binary files /dev/null and b/stuff/config/qss/standard/current.png differ diff --git a/stuff/config/qss/standard/down_arrow.png b/stuff/config/qss/standard/down_arrow.png new file mode 100644 index 0000000..f37e972 Binary files /dev/null and b/stuff/config/qss/standard/down_arrow.png differ diff --git a/stuff/config/qss/standard/down_arrow_black.png b/stuff/config/qss/standard/down_arrow_black.png new file mode 100644 index 0000000..d9fbc98 Binary files /dev/null and b/stuff/config/qss/standard/down_arrow_black.png differ diff --git a/stuff/config/qss/standard/down_arrow_disabled.png b/stuff/config/qss/standard/down_arrow_disabled.png new file mode 100644 index 0000000..d9eefed Binary files /dev/null and b/stuff/config/qss/standard/down_arrow_disabled.png differ diff --git a/stuff/config/qss/standard/flipmarker.png b/stuff/config/qss/standard/flipmarker.png new file mode 100644 index 0000000..a35bfd9 Binary files /dev/null and b/stuff/config/qss/standard/flipmarker.png differ diff --git a/stuff/config/qss/standard/flipslider.png b/stuff/config/qss/standard/flipslider.png new file mode 100644 index 0000000..f402f30 Binary files /dev/null and b/stuff/config/qss/standard/flipslider.png differ diff --git a/stuff/config/qss/standard/frame.png b/stuff/config/qss/standard/frame.png new file mode 100644 index 0000000..32e171f Binary files /dev/null and b/stuff/config/qss/standard/frame.png differ diff --git a/stuff/config/qss/standard/framedisable.png b/stuff/config/qss/standard/framedisable.png new file mode 100644 index 0000000..e81e80a Binary files /dev/null and b/stuff/config/qss/standard/framedisable.png differ diff --git a/stuff/config/qss/standard/framehover.png b/stuff/config/qss/standard/framehover.png new file mode 100644 index 0000000..38fc3a5 Binary files /dev/null and b/stuff/config/qss/standard/framehover.png differ diff --git a/stuff/config/qss/standard/ftab.png b/stuff/config/qss/standard/ftab.png new file mode 100644 index 0000000..155d4a8 Binary files /dev/null and b/stuff/config/qss/standard/ftab.png differ diff --git a/stuff/config/qss/standard/ftbtab.png b/stuff/config/qss/standard/ftbtab.png new file mode 100644 index 0000000..a16865f Binary files /dev/null and b/stuff/config/qss/standard/ftbtab.png differ diff --git a/stuff/config/qss/standard/fuptab.png b/stuff/config/qss/standard/fuptab.png new file mode 100644 index 0000000..5e11539 Binary files /dev/null and b/stuff/config/qss/standard/fuptab.png differ diff --git a/stuff/config/qss/standard/fuptbtab.png b/stuff/config/qss/standard/fuptbtab.png new file mode 100644 index 0000000..66dd2a6 Binary files /dev/null and b/stuff/config/qss/standard/fuptbtab.png differ diff --git a/stuff/config/qss/standard/h_chandle.png b/stuff/config/qss/standard/h_chandle.png new file mode 100644 index 0000000..aff3e52 Binary files /dev/null and b/stuff/config/qss/standard/h_chandle.png differ diff --git a/stuff/config/qss/standard/h_handle.png b/stuff/config/qss/standard/h_handle.png new file mode 100644 index 0000000..d8bdf6f Binary files /dev/null and b/stuff/config/qss/standard/h_handle.png differ diff --git a/stuff/config/qss/standard/h_handle_disabled.png b/stuff/config/qss/standard/h_handle_disabled.png new file mode 100644 index 0000000..57ba8e0 Binary files /dev/null and b/stuff/config/qss/standard/h_handle_disabled.png differ diff --git a/stuff/config/qss/standard/h_slider_left.png b/stuff/config/qss/standard/h_slider_left.png new file mode 100644 index 0000000..20afa79 Binary files /dev/null and b/stuff/config/qss/standard/h_slider_left.png differ diff --git a/stuff/config/qss/standard/h_slider_left_disabled.png b/stuff/config/qss/standard/h_slider_left_disabled.png new file mode 100644 index 0000000..d592b52 Binary files /dev/null and b/stuff/config/qss/standard/h_slider_left_disabled.png differ diff --git a/stuff/config/qss/standard/h_slider_right.png b/stuff/config/qss/standard/h_slider_right.png new file mode 100644 index 0000000..f5525ef Binary files /dev/null and b/stuff/config/qss/standard/h_slider_right.png differ diff --git a/stuff/config/qss/standard/h_slider_right_disabled.png b/stuff/config/qss/standard/h_slider_right_disabled.png new file mode 100644 index 0000000..63103af Binary files /dev/null and b/stuff/config/qss/standard/h_slider_right_disabled.png differ diff --git a/stuff/config/qss/standard/hsplitter_handle.png b/stuff/config/qss/standard/hsplitter_handle.png new file mode 100644 index 0000000..ad93550 Binary files /dev/null and b/stuff/config/qss/standard/hsplitter_handle.png differ diff --git a/stuff/config/qss/standard/left_arrow_black.png b/stuff/config/qss/standard/left_arrow_black.png new file mode 100644 index 0000000..b89cdcc Binary files /dev/null and b/stuff/config/qss/standard/left_arrow_black.png differ diff --git a/stuff/config/qss/standard/ltab.png b/stuff/config/qss/standard/ltab.png new file mode 100644 index 0000000..7fe6408 Binary files /dev/null and b/stuff/config/qss/standard/ltab.png differ diff --git a/stuff/config/qss/standard/ltbtab.png b/stuff/config/qss/standard/ltbtab.png new file mode 100644 index 0000000..f99cb00 Binary files /dev/null and b/stuff/config/qss/standard/ltbtab.png differ diff --git a/stuff/config/qss/standard/menuseparator.PNG b/stuff/config/qss/standard/menuseparator.PNG new file mode 100644 index 0000000..0a44fae Binary files /dev/null and b/stuff/config/qss/standard/menuseparator.PNG differ diff --git a/stuff/config/qss/standard/oouptab.png b/stuff/config/qss/standard/oouptab.png new file mode 100644 index 0000000..69df8e4 Binary files /dev/null and b/stuff/config/qss/standard/oouptab.png differ diff --git a/stuff/config/qss/standard/over.png b/stuff/config/qss/standard/over.png new file mode 100644 index 0000000..5ea84de Binary files /dev/null and b/stuff/config/qss/standard/over.png differ diff --git a/stuff/config/qss/standard/radio.png b/stuff/config/qss/standard/radio.png new file mode 100644 index 0000000..ef901a8 Binary files /dev/null and b/stuff/config/qss/standard/radio.png differ diff --git a/stuff/config/qss/standard/radio_p.png b/stuff/config/qss/standard/radio_p.png new file mode 100644 index 0000000..277940f Binary files /dev/null and b/stuff/config/qss/standard/radio_p.png differ diff --git a/stuff/config/qss/standard/right_arrow_black.png b/stuff/config/qss/standard/right_arrow_black.png new file mode 100644 index 0000000..8878c62 Binary files /dev/null and b/stuff/config/qss/standard/right_arrow_black.png differ diff --git a/stuff/config/qss/standard/sb_downarrow.png b/stuff/config/qss/standard/sb_downarrow.png new file mode 100644 index 0000000..cc9e76b Binary files /dev/null and b/stuff/config/qss/standard/sb_downarrow.png differ diff --git a/stuff/config/qss/standard/sb_h.png b/stuff/config/qss/standard/sb_h.png new file mode 100644 index 0000000..8ea48c5 Binary files /dev/null and b/stuff/config/qss/standard/sb_h.png differ diff --git a/stuff/config/qss/standard/sb_hhandle.png b/stuff/config/qss/standard/sb_hhandle.png new file mode 100644 index 0000000..d17bdc3 Binary files /dev/null and b/stuff/config/qss/standard/sb_hhandle.png differ diff --git a/stuff/config/qss/standard/sb_hline.png b/stuff/config/qss/standard/sb_hline.png new file mode 100644 index 0000000..a94e491 Binary files /dev/null and b/stuff/config/qss/standard/sb_hline.png differ diff --git a/stuff/config/qss/standard/sb_larrow.png b/stuff/config/qss/standard/sb_larrow.png new file mode 100644 index 0000000..eb9904e Binary files /dev/null and b/stuff/config/qss/standard/sb_larrow.png differ diff --git a/stuff/config/qss/standard/sb_rarrow.png b/stuff/config/qss/standard/sb_rarrow.png new file mode 100644 index 0000000..c13720c Binary files /dev/null and b/stuff/config/qss/standard/sb_rarrow.png differ diff --git a/stuff/config/qss/standard/sb_uparrow.png b/stuff/config/qss/standard/sb_uparrow.png new file mode 100644 index 0000000..64f2da3 Binary files /dev/null and b/stuff/config/qss/standard/sb_uparrow.png differ diff --git a/stuff/config/qss/standard/sb_v.png b/stuff/config/qss/standard/sb_v.png new file mode 100644 index 0000000..5822808 Binary files /dev/null and b/stuff/config/qss/standard/sb_v.png differ diff --git a/stuff/config/qss/standard/sb_vhandle.png b/stuff/config/qss/standard/sb_vhandle.png new file mode 100644 index 0000000..a1147e0 Binary files /dev/null and b/stuff/config/qss/standard/sb_vhandle.png differ diff --git a/stuff/config/qss/standard/sb_vline.png b/stuff/config/qss/standard/sb_vline.png new file mode 100644 index 0000000..2ac4415 Binary files /dev/null and b/stuff/config/qss/standard/sb_vline.png differ diff --git a/stuff/config/qss/standard/sl_groove.png b/stuff/config/qss/standard/sl_groove.png new file mode 100644 index 0000000..37c82ed Binary files /dev/null and b/stuff/config/qss/standard/sl_groove.png differ diff --git a/stuff/config/qss/standard/standard.qss b/stuff/config/qss/standard/standard.qss new file mode 100644 index 0000000..9796129 --- /dev/null +++ b/stuff/config/qss/standard/standard.qss @@ -0,0 +1,1020 @@ +/* TOONZ 6.1 Qt Style Test + Digital Video 2007 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(90,90,90); + background-color: rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +TPanel { + qproperty-BGColor: rgb(235,235,235); + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-color: rgb(225,225,225); +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(225,225,225); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: gray; + background-color: rgb(225,225,225); +} +QMenu { + margin: 2px; + background-color: rgb(225,225,225); + border: 1px solid black; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/standard/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +QMenu::item:disabled { + color: gray; + background-color: rgb(225,225,225); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/standard/chk_unchk.png); +} +QMenu::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QMenu::indicator:checked { + image: url(qss/standard/chk_chk.png); +} +QMenu::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + background-color: rgb(225,225,225); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background-color: rgb(235,235,235); +} +QFrame:disabled { +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(120,120,120); + background-color: rgb(225,225,225); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(225,225,225); +} +QDialog #dialogMainFrame { + background-color: rgb(235,235,235); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(120,120,120); +} +#styleEditorPage { + border: 0px; +} +StyleEditor QToolBar { + border-right: 1px solid rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background-color: rgb(210,210,210); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background-color: rgb(160,160,160); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(225,225,225); +} +#dvTopBar { + background-color: rgb(225,225,225); +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background-color: rgb(225,225,225); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: white; +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background-color: rgb(210,210,210); + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-BGColor: rgb(230,230,230); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +#ScrollArea { + border:1px solid rgb(150, 150, 150); +} +#CellScrollArea { + border-top:1px solid rgb(150, 150, 150); + border-left:1px solid rgb(150, 150, 150); +} +/* XSheet scrollAreas (row, column and cell) */ +XsheetViewer { + background-color: rgb(210,210,210); + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-BGColor: rgb(230,230,230); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +#xsheetScrollArea { + background-color: rgb(210,210,210); + margin: 1px; +} +#cornerWidget { + background-color: rgb(210,210,210); +} +#XshColumnChangeObjectWidget { + background-color: rgb(235,235,235); + selection-background-color: rgb(49,106,197); + border: 1px solid black +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(225,225,225); + qproperty-ValueLineColor: rgb(186,186,186); + qproperty-FrameLineColor: rgb(210,210,210); + qproperty-OtherCurvesColor: rgb(150,150,150); + qproperty-RulerBackground: rgb(255,255,255); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(120,120,120); + background-color: rgb(225,225,225); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/standard/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(225,225,225); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar (vertical)*/ +/* 6.4: #toolBar is now a scrollable container of the actual QToolBar */ +#toolBar { + margin: 1px; + background-color: rgb(225,225,225); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(120,120,120); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(225,225,225); +} +#toolBar QToolButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/standard/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(235,235,235); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(120,120,120); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(225,225,225); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(235,235,235); + border: 1px solid rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/standard/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/standard/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/standard/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + border-image: url(qss/standard/but.png) 2; + border-width: 2; + color: black; +} +/*---------------------------------------------------------------------------*/ +#PeggingWidget { + background-color: rgb(0,0,0); +} +/*---------------------------------------------------------------------------*/ +#PeggingButton { + border-image: none; + background-color: rgb(228,228,228); + border: 0px solid black; +} +#PeggingButton:hover { + border-image: url(qss/standard/over.png) 2; + border-width: 2; +} +#PeggingButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#PeggingButton:checked { + border-image: none; + background-color: rgb(255,255,255); + border-left-color: lightgray; + border-left-width: 2px; + border-top-color: lightgray; + border-top-width: 2px; + border-right-width: 0px; + border-bottom-width: 0px; +} +/*---------------------------------------------------------------------------*/ +QComboBox[editable="false"] { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/standard/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(120,120,120); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/standard/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; +} +QComboBox[editable="false"]::drop-down { + width: 15px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(225,225,225); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(245,245,245); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(215,215,215); +} +#ScrollLeftButton { + image: url(qss/standard/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/standard/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/standard/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/standard/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QToolButton:disabled { + border-image: url(qss/standard/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(235,235,235); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/standard/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/standard/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + border-image: url(qss/standard/frame.png) 3; + border-width: 3; +} +QLineEdit::focus { + border-image: url(qss/standard/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(128,128,128); + border-image: url(qss/standard/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit*/ +QPlainTextEdit, QTextEdit { + border-image: url(qss/standard/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/standard/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit::disabled { + color: rgb(128,128,128); + border-image: url(qss/standard/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; +} +QGroupBox:enabled +{ + border-color: rgb(120, 120, 120); +} +QGroupBox:disabled +{ + border-color: rgb(180, 180, 180); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/standard/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal:enabled { + image: url(qss/standard/h_handle.png); + width: 16px; + margin: -16px -4px; +} +QSlider::handle:horizontal:disabled { + image: url(qss/standard/h_handle_disabled.png); + width: 16px; + margin: -16px -4px; +} + + +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/standard/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/standard/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/standard/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/standard/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/standard/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/standard/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/standard/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/standard/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/standard/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + + /*Fixed height of the slider. It's a Q_PROPERTY since on mac the min-height/max-height trick seems + to be not working*/ + qproperty-PBHeight: 10; + + /*Overlay and marker appearences*/ + qproperty-PBOverlay: url(qss/standard/flipslider.png); + qproperty-PBMarker: url(qss/standard/flipmarker.png); + + /*Margins for the colored frame indicators and the marker. + NOTE: The above images are cut in 3 pieces according to the left-right margin components. The margin + parts are reproduced identically at the sides of the slider colorbar. + The middle part is stretched to cover the slider width.*/ + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + /*The marker margins are shown at the sides of the colorbar rect corresponding to a frame.*/ + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + /*Colors shown for the colorbar. The base color is commonly used outside render contexts. + The others refer to the status of the render for single frames.*/ + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/standard/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/standard/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/standard/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/standard/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/standard/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/standard/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(160,160,160); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/standard/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/standard/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/standard/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/standard/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/standard/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/standard/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/standard/sb_hhandle.png)3; + border-width: 3; + image: url(qss/standard/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/standard/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/standard/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/standard/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/standard/sb_vhandle.png)3; + border-width: 3; + image: url(qss/standard/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/standard/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/standard/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/standard/chk_unchk.png); +} +QCheckBox::indicator:unchecked:disabled, QTreeView::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/standard/chk_chk.png); +} +QCheckBox::indicator:checked:disabled, QTreeView::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/standard/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/standard/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(235,235,235); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(225,225,225); +} +QSplitter::handle:horizontal { + image: url(qss/standard/hsplitter_handle.png); + border-left: 1px solid rgb(120,120,120); + border-right: 1px solid rgb(120,120,120); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/standard/vsplitter_handle.png); + border-top: 1px solid rgb(120,120,120); + border-bottom: 1px solid rgb(120,120,120); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(225,225,225); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; +} diff --git a/stuff/config/qss/standard/standard_mac.qss b/stuff/config/qss/standard/standard_mac.qss new file mode 100644 index 0000000..d784a0c --- /dev/null +++ b/stuff/config/qss/standard/standard_mac.qss @@ -0,0 +1,1054 @@ +/* TOONZ 6.1 Qt Style Test + Digital Video 2007 +*/ + +/*---------------------------------------------------------------------------*/ +QMainWindow #MainStackedWidget { + border: 6px solid rgb(90,90,90); + background-color: rgb(90,90,90); +} +/*---------------------------------------------------------------------------*/ +/*------------ Mac OS ------------*/ +TPanel { + qproperty-BGColor: rgb(235,235,235); + background-color: rgb(235,235,235); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +.QWidget { +} +/*---------------------------------------------------------------------------*/ +/* QMenuBar */ +QMenuBar { + background-color: rgb(225,225,225); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +/*Menu*/ +#fxMenu::item:checked, #fxMenu::item:unchecked { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + background-color: rgb(225,225,225); + color: black; + font-weight: bold; +} +#fxMenu::indicator:unchecked { + image: url(); +} +#fxMenu::indicator:checked { + image: url(); +} +#fxMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +#fxMenu::item:disabled { + color: gray; + background-color: rgb(225,225,225); +} +QMenu { + margin: 2px; + background-color: rgb(225,225,225); + border: 1px solid black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QMenu::item { + padding: 2px 25px 2px 20px; + border: 1px solid transparent; + color: black; +} +QMenu::separator { + background-image: url(qss/standard/menuseparator.png) 1; + height: 2px; + margin-bottom: 2px; + margin-top: 2px; + margin-left: 5px; + margin-right: 5px; +} +QMenu::item:selected { + background-color: rgb(49,106,197); + color: white; + border-color: darkblue; +} +QMenu::item:disabled { + color: gray; + background-color: rgb(225,225,225); + border: 1px solid transparent; +} +QMenu::indicator { + width: 13px; + height: 13px; +} +QMenu::indicator:unchecked { + image: url(qss/standard/chk_unchk.png); +} +QMenu::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QMenu::indicator:checked { + image: url(qss/standard/chk_chk.png); +} +QMenu::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +QPushButton { + margin-bottom: -1px; + border-image: url(qss/standard/but.png) 2; + border-width: 2; + color: black; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +/*---------------------------------------------------------------------------*/ +/* Generic QFrame */ +QFrame { + border: 0px; + margin: 0px; + background-color: rgb(235,235,235); + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + +} +QFrame:disabled { +} +#OnePixelMarginFrame { + margin: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QDialog */ +QDialog #dialogButtonFrame { + border-top: 1px solid rgb(120,120,120); + background-color: rgb(225,225,225); +} +QDialog #dialogButtonFrame QLabel { + background-color: rgb(225,225,225); +} +QDialog #dialogMainFrame { + background-color: rgb(235,235,235); + border: none; +} +/*---------------------------------------------------------------------------*/ +/* Special setting for the styleEditor */ +StyleEditor .QScrollArea { + margin: 0px; + border-right: 1px solid rgb(120,120,120); +} +#styleEditorPage { + border: 0px; +} +StyleEditor QToolBar { + border-right: 1px solid rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/*Panel title bar*/ +#HorizontalPanelTitleBar { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_horizontal.png) 8; +} +#VerticalPanelTitleBar[Active="true"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_vertical.png) 8; +} +#VerticalPanelTitleBar[Active="false"] { + background-color: transparent; + border-width: 8px; + border-image: url(qss/standard/titlebar_vertical_inactive.png) 8; +} +/*---------------------------------------------------------------------------*/ +#SchematicSceneViewer { + background-color: rgb(210,210,210); +} +/*---------------------------------------------------------------------------*/ +/*SceneViewer Ruler*/ +Ruler { + qproperty-ParentBGColor: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* Menu bar */ +#subXsheetTabBarContainer { + background-color: rgb(160,160,160); +} +SubSheetBar { + qproperty-ParentBGColor: rgb(225,225,225); +} +#dvTopBar { + background-color: rgb(225,225,225); +} +/*---------------------------------------------------------------------------*/ +/* Tool bar */ +#ToolBarContainer { + background-color: rgb(225,225,225); +} +/*---------------------------------------------------------------------------*/ +/* Tab bar */ +#TabBarContainer { + border: none; + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* File Browser Tree and Cast Tree background*/ +#BrowserTreeView { + background-color: white; +} +/*---------------------------------------------------------------------------*/ +/* Customize sheet scrollArea */ +/* Sheet scrollAreas. + NB.: SheetViewer e XsheetViewer va reso un unico oggetto + una volta che si e' uniformato completamente l'xsheet alle colonne numeriche.*/ +SpreadsheetViewer { + background-color: rgb(210,210,210); + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-BGColor: rgb(230,230,230); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +#ScrollArea { + border:1px solid rgb(150, 150, 150); +} +#CellScrollArea { + border-top:1px solid rgb(150, 150, 150); + border-left:1px solid rgb(150, 150, 150); +} +/* XSheet scrollAreas (row, column and cell) */ +XsheetViewer { + background-color: rgb(210,210,210); + qproperty-LightLightBGColor: rgb(250,250,250); + qproperty-LightBGColor: rgb(240,240,240); + qproperty-BGColor: rgb(230,230,230); + qproperty-DarkBGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +#xsheetScrollArea { + background-color: rgb(210,210,210); + margin: 1px; +} +#cornerWidget { + background-color: rgb(210,210,210); +} +#XshColumnChangeObjectWidget { + background-color: rgb(235,235,235); + selection-background-color: rgb(49,106,197); + border: 1px solid black +} +/*---------------------------------------------------------------------------*/ +/* Customize Function Area */ +FunctionPanel { + qproperty-BGColor: rgb(225,225,225); + qproperty-ValueLineColor: rgb(186,186,186); + qproperty-FrameLineColor: rgb(210,210,210); + qproperty-OtherCurvesColor: rgb(150,150,150); + qproperty-RulerBackground: rgb(255,255,255); +} +/*---------------------------------------------------------------------------*/ +/* Customize FilmStrip color */ +FilmstripFrames { + qproperty-BGColor: rgb(225,225,225); + qproperty-LightLineColor: rgb(210,210,210); + qproperty-DarkLineColor: rgb(150,150,150); +} +/*---------------------------------------------------------------------------*/ +/* Main toolbar */ +QToolBar { + /* top right bottom left */ + margin: 0px; + padding: 0px; + border-top: 1px solid rgb(120,120,120); + background-color: rgb(225,225,225); + border-image: none; +} +QToolBar::separator { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 0px; + image: url(qss/standard/bottomseparator.png); + width: 3px; +} +QToolBar QToolButton[checkable=false] { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QToolButton:disabled { + border-image: none; +} +QToolBar QToolButton[checkable=false]:hover { + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +QToolBar QToolButton[checkable=false]:checked, +QToolBar QToolButton[checkable=false]:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*Chackable buttons: NO border image*/ +QToolBar QToolButton[checkable=true] { + /* top right bottom left */ + margin: 1px -3px 0px -3px; + border: 0px solid transparent; + border-image: none; +} +QToolBar QToolButton[checkable=true]:hover { + image: none; +} +QToolBar QToolButton[checkable=true]:checked, +QToolBar QToolButton[checkable=true]:pressed { + border-image: none; +} +QToolBar QToolButton#chackableButtonWithImageBorder { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 2px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QToolButton#chackableButtonWithImageBorder:hover { + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +QToolBar QToolButton#chackableButtonWithImageBorder:checked, +QToolBar QToolButton#chackableButtonWithImageBorder:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +QToolBar QToolButton::menu-button { + margin-top: 1px; + border-image: none; + background-color: rgb(225,225,225); +} +QToolBar QLabel { + margin-top: 1px; + background-color: rgb(225,225,225); + border-width: 2; +} +/* To hide Arrow !! */ +QToolBar QToolButton::menu-indicator { + image: none; +} +/*---------------------------------------------------------------------------*/ +/* Tool Bar (vertical)*/ +/* 6.4: #toolBar is now a scrollable container of the actual QToolBar */ +#toolBar { + margin: 1px; + background-color: rgb(225,225,225); +} +#toolBar QToolBar { + border: none; + margin: 1px; +} +#toolBar QToolBar::separator { + image: none; + margin: 0px 1px 0px 1px; + background-color: rgb(120,120,120); + height: 1px; +} +#toolBar QToolButton { + margin: 0px; + border: 2px solid transparent; + border-image: none; + background-color: rgb(225,225,225); +} +#toolBar QToolButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#toolBar QToolButton:checked { + border-image: url(qss/standard/current.png) 2; + border-width: 2; +} +#toolBar QPushButton { + margin: 0px; + border: 1px solid black; +} +/*---------------------------------------------------------------------------*/ +/* ToolOptionPanel ToolBar */ +#toolOptionBar { + border: 0px; + margin: 0px; + background-color: rgb(235,235,235); +} +#toolOptionBar::separator { + image: none; + margin-top: 0px; + margin-bottom: 0px; + margin-left: 1px; + margin-right: 1px; + background-color: rgb(120,120,120); + height: 30px; + width: 1px; +} +#toolOptionBar QWidget { + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/*Key frame navigator*/ +#keyFrameNavigator QToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; + background-color: rgb(225,225,225); +} +#keyFrameNavigator QToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +#keyFrameNavigator QToolButton:checked, +#keyFrameNavigator QToolButton:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +QMessageBox { + background-color: rgb(235,235,235); + border: 1px solid rgb(120,120,120); +} +/*---------------------------------------------------------------------------*/ +/* Customize arrows. */ +QComboBox::down-arrow, QComboBox::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::down-arrow:disabled, QComboBox::down-arrow:off { + image: url(qss/standard/down_arrow_disabled.png); +} +QComboBox::up-arrow { + image: url(qss/standard/up_arrow.png); + width: 7px; + height: 7px; +} +QComboBox::up-arrow:disabled, QComboBox::up-arrow:off { + image: url(qss/standard/up_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*PopupButton*/ +PopupButton { + margin: 0px; + padding-left:-8px; + padding-right:4px; +} +PopupButton QMenu { + background-color: rgb(225,225,225); + border: 0px; + padding: -1px; +} +PopupButton QMenu::item { + border: 1px solid transparent; + color: black; +} +PopupButton::menu-indicator { + image: url(qss/standard/down_arrow.png); + width: 10px; + height: 17px; + border-left: 1px solid grey; +} +PopupButton::menu-indicator:disabled { + image: url(qss/standard/down_arrow_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/*Outline Style PopupButtons*/ +PopupButton#Cap { + min-width: 32px; max-width: 32px; +} +PopupButton#Cap QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Cap QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +PopupButton#Join { + min-width: 32px; max-width: 32px; +} +PopupButton#Join QMenu { + min-width: 18px; max-width: 18px; +} +PopupButton#Join QMenu::item { + min-width: 16px; max-width: 16px; + padding: 0px; +} +/*---------------------------------------------------------------------------*/ +#PeggingWidget { + background-color: rgb(0,0,0); +} +/*---------------------------------------------------------------------------*/ +#PeggingButton { + border-image: none; + background-color: rgb(228,228,228); + border: 0px solid black; +} +#PeggingButton:hover { + border-image: url(qss/standard/over.png) 2; + border-width: 2; +} +#PeggingButton:pressed { + border-image: url(qss/standard/click.png) 2; + border-width: 2; +} +#PeggingButton:checked { + border-image: none; + background-color: rgb(255,255,255); + border-left-color: lightgray; + border-left-width: 2px; + border-top-color: lightgray; + border-top-width: 2px; + border-right-width: 0px; + border-bottom-width: 0px; +} +/*---------------------------------------------------------------------------*/ +QComboBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QComboBox[editable="false"] { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QComboBox[editable="true"] { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QComboBox:disabled, QPushButton:disabled { + border-image: url(qss/standard/butdis.png) 2; + border-width: 2; + height: 22px; + color: rgb(120,120,120); +} +QComboBox:hover, QPushButton:hover { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; + height: 22px; +} +QComboBox:pressed, QPushButton:pressed { + border-image: url(qss/standard/but.png) 2; + border-width: 2; + height: 22px; +} +/* Customize non-editable comboboxes drop-down */ +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 2px; +} +QComboBox[editable="false"]::drop-down { + width: 15px; + border-left-style: solid; + border-left-color: darkgray; + border-left-width: 1px; +} +/* Customize editable comboboxes.*/ +QComboBox[editable="true"] { + padding-right: 16px; +} +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} +/*---------------------------------------------------------------------------*/ +/* The animated, scrollable toolbar containers */ +DvScrollWidget > QPushButton { + border-image: none; + border: 0px solid black; + background-color: rgb(225,225,225); +} +DvScrollWidget > QPushButton:hover { + border-image: none; + border: 0px solid black; + background-color: rgb(245,245,245); +} +DvScrollWidget > QPushButton:pressed { + border-image: none; + border: 0px solid black; + background-color: rgb(215,215,215); +} +#ScrollLeftButton { + image: url(qss/standard/left_arrow_black.png); + border-right: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollRightButton { + image: url(qss/standard/right_arrow_black.png); + border-left: 1px solid black; + min-width: 15px; + max-width: 15px; +} +#ScrollUpButton { + image: url(qss/standard/up_arrow_black.png); + border-bottom: 1px solid black; + min-height: 15px; + max-height: 15px; +} +#ScrollDownButton { + image: url(qss/standard/down_arrow_black.png); + border-top: 1px solid black; + min-height: 15px; + max-height: 15px; +} +/* Flipbook toolbar-specific */ +#ToolBarContainer #ScrollLeftButton { + margin-top: 1px; +} +#ToolBarContainer #ScrollRightButton { + margin-top: 1px; +} +/*---------------------------------------------------------------------------*/ +/* QToolButton */ +QToolButton { + border-image: url(qss/standard/but.png) 2; + border-width: 2; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QToolButton:disabled { + border-image: url(qss/standard/butdis.png) 2; + border-width: 2; +} +QToolButton:hover { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; +} +QToolButton:disabled:hover { + margin-top: 1px; + border-image: none; + border-width: 0px; +} +QToolButton:pressed { + border-image: url(qss/standard/but.png) 2; + border-width: 2; +} +QToolButton:checked { + border-image: url(qss/standard/buthover.png) 2; + border-width: 2; +} +/*---------------------------------------------------------------------------*/ +#ToolbarToolButton { + /* top right bottom left */ + margin: 1px 0px 0px 0px; + border: 2px solid transparent; + padding-right: 1px; + border-image: none; +} +#ToolbarToolButton:hover { + margin: 1px 0px 0px 0px; + border-width: 2; + border-image: url(qss/standard/over.png) 2; +} +#ToolbarToolButton:checked, +#ToolbarToolButton:pressed { + border-width: 2; + border-image: url(qss/standard/click.png) 2; +} +/*---------------------------------------------------------------------------*/ +#StyleEditorArrowButton { + margin: 0px 0px 0px 0px; + border: 1px solid transparent; + border-image: none; + background-color: rgb(235,235,235); +} +#StyleEditorArrowButton:hover { + border-width: 1; + border-image: url(qss/standard/over.png) 1; +} +#StyleEditorArrowButton:checked, +#StyleEditorArrowButton:pressed { + border-width: 1; + border-image: url(qss/standard/click.png) 1; +} +/*---------------------------------------------------------------------------*/ +/* QLineEdit */ +QLineEdit { + margin-bottom: 1px; + margin-right: 1px; + border-image: url(qss/standard/frame.png) 3; + border-width: 3; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QLineEdit::focus { + border-image: url(qss/standard/framehover.png) 3; + border-width: 3; +} +QLineEdit:disabled { + color: rgb(128,128,128); + border-image: url(qss/standard/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QPlainTextEdit & QTextEdit*/ +QPlainTextEdit, QTextEdit { + border-image: url(qss/standard/frame.png) 3; + border-width: 3; +} +QPlainTextEdit::focus, QTextEdit::focus { + border-image: url(qss/standard/framehover.png) 3; + border-width: 3; +} +QPlainTextEdit:disabled, QTextEdit::disabled { + color: rgb(128,128,128); + border-image: url(qss/standard/framedisable.png) 3; +} +/*---------------------------------------------------------------------------*/ +/* QGroupBox */ +QGroupBox +{ + background-color: none; + margin-top: 5px; + padding-top: 5px; + border: 1px solid; + border-radius: 3px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QGroupBox:enabled +{ + border-color: rgb(120, 120, 120); +} +QGroupBox:disabled +{ + border-color: rgb(180, 180, 180); +} +QGroupBox::title { + subcontrol-origin: border; + top: -6px; + left: 6px; +} +/*---------------------------------------------------------------------------*/ +/* QStatusBar */ +QStatusBar { + background-color: rgb(235,235,235); +} +/*---------------------------------------------------------------------------*/ +/* Customize Horizontal QSlider. */ +QSlider { + height: 20; +} +QSlider::groove:horizontal { + border-image: url(qss/standard/sl_groove.png) 1; + border-width: 1; + height: 1px; +} +QSlider::handle:horizontal:enabled { + image: url(qss/standard/h_handle.png); + width: 16px; + margin: -16px -4px; +} +QSlider::handle:horizontal:disabled { + image: url(qss/standard/h_handle_disabled.png); + width: 16px; + margin: -16px -4px; +} + + +/* Customize Horizontal QSlider that have name "colorSlider" */ +#colorSlider::groove:horizontal { + border-image: none; +} +#colorSlider::handle:horizontal { + image: url(qss/standard/h_chandle.png); + width: 16px; + margin: -16px -3px -16px -4px; +} + +/*---------------------------------------------------------------------------*/ + +#IntPairField { + qproperty-HandleLeftPixmap: url(qss/standard/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/standard/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/standard/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/standard/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +#DoublePairField { + qproperty-HandleLeftPixmap: url(qss/standard/h_slider_left.png); + qproperty-HandleRightPixmap: url(qss/standard/h_slider_right.png); + qproperty-HandleLeftGrayPixmap: url(qss/standard/h_slider_left_disabled.png); + qproperty-HandleRightGrayPixmap: url(qss/standard/h_slider_right_disabled.png); +} + +/*---------------------------------------------------------------------------*/ + +/* Flipbook Slider. */ +FlipSlider { + qproperty-PBHeight: 10; + + qproperty-PBOverlay: url(qss/standard/flipslider.png); + qproperty-PBMarker: url(qss/standard/flipmarker.png); + + qproperty-PBColorMarginLeft: 2; + qproperty-PBColorMarginTop: 2; + qproperty-PBColorMarginRight: 2; + qproperty-PBColorMarginBottom: 3; + + qproperty-PBMarkerMarginLeft: 2; + qproperty-PBMarkerMarginRight: 2; + + qproperty-baseColor: rgb(235,235,235); + qproperty-notStartedColor: rgb(210,40,40); + qproperty-startedColor: rgb(220,160,160); + qproperty-finishedColor: rgb(235,235,235); +} + +/*---------------------------------------------------------------------------*/ +/* Customize QTabBar */ +QTabBar { + height: 22px; + border: 0px; +} +QTabBar::tab { + top: -1px; + min-width: 54px; + min-height: 15px; +} +QTabBar::tab:selected { + padding-top: 2px; + border-image: url(qss/standard/uptab.png) 4; + border-width: 4; +} +QTabBar::tab:only-one { + padding-top: 2px; + border-image: url(qss/standard/oouptab.png) 4; + border-width: 4; +} +QTabBar::tab:first:selected { + border-image: url(qss/standard/fuptab.png) 4; + border-width: 4; +} +QTabBar::tab:!selected { + margin-top: 2px; + border-image: url(qss/standard/tab.png) 4; + border-width: 4; +} +QTabBar::tab:first:!selected { + border-image: url(qss/standard/ftab.png) 4; + border-width: 4; +} +QTabBar::tab:last:!selected { + border-image: url(qss/standard/ltab.png) 4; + border-width: 4; +} +/* Customize RoomTabWidget */ +/* ATTENTION: These declarations must be putted after the QTabBar ones */ +RoomTabWidget { + height: 22px; + background-color: rgb(160,160,160); + color: white; +} +RoomTabWidget::tab { + top: 1px; + min-width: 54px; + min-height: 15px; +} +RoomTabWidget::tab:selected { + border-image: url(qss/standard/uptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:first:selected, RoomTabWidget::tab:only-one { + border-image: url(qss/standard/fuptbtab.png) 4; + border-width: 4; + font: bold; +} +RoomTabWidget::tab:!selected { + margin-top: 2px; + border-image: url(qss/standard/tbtab.png) 4; + border-width: 4; + font: normal; +} +RoomTabWidget::tab:first:!selected { + border-image: url(qss/standard/ftbtab.png) 4; + border-width: 4; +} +RoomTabWidget::tab:last:!selected { + border-image: url(qss/standard/ltbtab.png) 4; + border-width: 4; +} +/*---------------------------------------------------------------------------*/ +/* Customize QScrollBar horizontal*/ +QScrollBar:horizontal { + background-image: url(qss/standard/sb_h.png); + height: 16px; + margin-top: 0px; + margin-bottom: 0px; + margin-right:15px; + margin-left:15px; +} +QScrollBar::handle:horizontal { + border-image: url(qss/standard/sb_hhandle.png)3; + border-width: 3; + image: url(qss/standard/sb_hline.png); + image-position: center center; +} +QScrollBar::add-line:horizontal { + image: url(qss/standard/sb_rarrow.png); + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + image: url(qss/standard/sb_larrow.png); + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} +/* Customize QScrollBar vertical*/ +QScrollBar:vertical { + background-image: url(qss/standard/sb_v.png); + width: 16px; + margin-left: 0px; + margin-right: 0px; + margin-top:15px; + margin-bottom:15px; +} +QScrollBar::handle:vertical { + border-image: url(qss/standard/sb_vhandle.png)3; + border-width: 3; + image: url(qss/standard/sb_vline.png); + image-position: center center; +} +QScrollBar::add-line:vertical { + image: url(qss/standard/sb_downarrow.png); + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} +QScrollBar::sub-line:vertical { + image: url(qss/standard/sb_uparrow.png); + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; +} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} +/*---------------------------------------------------------------------------*/ +/* Customize check boxes. */ +QCheckBox { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + spacing: 1px; +} +QCheckBox::indicator { + width: 13px; + height: 13px; +} +QCheckBox::indicator:unchecked, QTreeView::indicator:unchecked { + image: url(qss/standard/chk_unchk.png); +} +QCheckBox::indicator:unchecked:disabled, QTreeView::indicator:unchecked:disabled { + image: url(qss/standard/chk_unchk_disabled.png); +} +QCheckBox::indicator:checked, QTreeView::indicator:checked { + image: url(qss/standard/chk_chk.png); +} +QCheckBox::indicator:checked:disabled, QTreeView::indicator:checked:disabled { + image: url(qss/standard/chk_chk_disabled.png); +} +/*---------------------------------------------------------------------------*/ +/* Customize radio buttons. */ +QRadioButton { + spacing: 5px; + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; +} +QRadioButton::indicator { + width: 13px; + height: 13px; +} +QRadioButton::indicator::unchecked { + image: url(qss/standard/radio.png); +} +QRadioButton::indicator::checked { + image: url(qss/standard/radio_p.png); +} +/*---------------------------------------------------------------------------*/ +/* QSplitter (Un pochettino assumo che sono tutti verticali) */ +QSplitter { + background-color: rgb(235,235,235); +} +QSplitter::handle { + margin: 0px; + background-color: rgb(225,225,225); +} +QSplitter::handle:horizontal { + image: url(qss/standard/hsplitter_handle.png); + border-left: 1px solid rgb(120,120,120); + border-right: 1px solid rgb(120,120,120); + image-position: center center; +} +QSplitter::handle:vertical { + image: url(qss/standard/vsplitter_handle.png); + border-top: 1px solid rgb(120,120,120); + border-bottom: 1px solid rgb(120,120,120); + padding-left: 1px; + padding-right: 1px; + image-position: center center; +} +/*---------------------------------------------------------------------------*/ +/* QToolTip */ +QToolTip { + /*Mac OS font style*/ + font-family: "Lucida Grande"; + font-size: 12px; + border: 1px solid black; + background-color: rgb(255,255,225); +} +/*---------------------------------------------------------------------------*/ +/* Generic Separators with inscribed name */ +DVGui--Separator { + alternate-background-color: rgb(120, 120, 120); /* line color */ +} +/*---------------------------------------------------------------------------*/ +/* Export Level Popup Options */ +#ExportLevelOptions +{ + background-color: rgb(225,225,225); +} +#ExportLevelOptions QWidget +{ + background-color: none; +} +ExportLevelPopup DVGui--Separator { + padding: 0px 5px 0px 5px; +} diff --git a/stuff/config/qss/standard/tab.png b/stuff/config/qss/standard/tab.png new file mode 100644 index 0000000..8a437d3 Binary files /dev/null and b/stuff/config/qss/standard/tab.png differ diff --git a/stuff/config/qss/standard/tbtab.png b/stuff/config/qss/standard/tbtab.png new file mode 100644 index 0000000..2ba38e7 Binary files /dev/null and b/stuff/config/qss/standard/tbtab.png differ diff --git a/stuff/config/qss/standard/titlebar_horizontal.png b/stuff/config/qss/standard/titlebar_horizontal.png new file mode 100644 index 0000000..5fe4bea Binary files /dev/null and b/stuff/config/qss/standard/titlebar_horizontal.png differ diff --git a/stuff/config/qss/standard/titlebar_vertical.png b/stuff/config/qss/standard/titlebar_vertical.png new file mode 100644 index 0000000..d2d9ad8 Binary files /dev/null and b/stuff/config/qss/standard/titlebar_vertical.png differ diff --git a/stuff/config/qss/standard/titlebar_vertical_inactive.png b/stuff/config/qss/standard/titlebar_vertical_inactive.png new file mode 100644 index 0000000..2ceee58 Binary files /dev/null and b/stuff/config/qss/standard/titlebar_vertical_inactive.png differ diff --git a/stuff/config/qss/standard/up_arrow.png b/stuff/config/qss/standard/up_arrow.png new file mode 100644 index 0000000..142e60d Binary files /dev/null and b/stuff/config/qss/standard/up_arrow.png differ diff --git a/stuff/config/qss/standard/up_arrow_black.png b/stuff/config/qss/standard/up_arrow_black.png new file mode 100644 index 0000000..6c27909 Binary files /dev/null and b/stuff/config/qss/standard/up_arrow_black.png differ diff --git a/stuff/config/qss/standard/up_arrow_disabled.png b/stuff/config/qss/standard/up_arrow_disabled.png new file mode 100644 index 0000000..4d2c277 Binary files /dev/null and b/stuff/config/qss/standard/up_arrow_disabled.png differ diff --git a/stuff/config/qss/standard/uptab.png b/stuff/config/qss/standard/uptab.png new file mode 100644 index 0000000..d8e2b0f Binary files /dev/null and b/stuff/config/qss/standard/uptab.png differ diff --git a/stuff/config/qss/standard/uptbtab.png b/stuff/config/qss/standard/uptbtab.png new file mode 100644 index 0000000..aa7826a Binary files /dev/null and b/stuff/config/qss/standard/uptbtab.png differ diff --git a/stuff/config/qss/standard/vsplitter_handle.png b/stuff/config/qss/standard/vsplitter_handle.png new file mode 100644 index 0000000..a1e7564 Binary files /dev/null and b/stuff/config/qss/standard/vsplitter_handle.png differ diff --git a/stuff/config/versioncontrol.xml b/stuff/config/versioncontrol.xml new file mode 100644 index 0000000..2dff9bb --- /dev/null +++ b/stuff/config/versioncontrol.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/stuff/doc/AddIno.pdf b/stuff/doc/AddIno.pdf new file mode 100644 index 0000000..5ad8a6e Binary files /dev/null and b/stuff/doc/AddIno.pdf differ diff --git a/stuff/doc/BlurIno.pdf b/stuff/doc/BlurIno.pdf new file mode 100644 index 0000000..a6f6219 Binary files /dev/null and b/stuff/doc/BlurIno.pdf differ diff --git a/stuff/doc/ChannelSelectorIno.pdf b/stuff/doc/ChannelSelectorIno.pdf new file mode 100644 index 0000000..2ee6493 Binary files /dev/null and b/stuff/doc/ChannelSelectorIno.pdf differ diff --git a/stuff/doc/ColorBurnIno.pdf b/stuff/doc/ColorBurnIno.pdf new file mode 100644 index 0000000..0bb138a Binary files /dev/null and b/stuff/doc/ColorBurnIno.pdf differ diff --git a/stuff/doc/ColorDodgeIno.pdf b/stuff/doc/ColorDodgeIno.pdf new file mode 100644 index 0000000..347e809 Binary files /dev/null and b/stuff/doc/ColorDodgeIno.pdf differ diff --git a/stuff/doc/CrossDissolveIno.pdf b/stuff/doc/CrossDissolveIno.pdf new file mode 100644 index 0000000..c9c5923 Binary files /dev/null and b/stuff/doc/CrossDissolveIno.pdf differ diff --git a/stuff/doc/DarkenIno.pdf b/stuff/doc/DarkenIno.pdf new file mode 100644 index 0000000..f5a4159 Binary files /dev/null and b/stuff/doc/DarkenIno.pdf differ diff --git a/stuff/doc/DarkerColorIno.pdf b/stuff/doc/DarkerColorIno.pdf new file mode 100644 index 0000000..e8868b3 Binary files /dev/null and b/stuff/doc/DarkerColorIno.pdf differ diff --git a/stuff/doc/DensityIno.pdf b/stuff/doc/DensityIno.pdf new file mode 100644 index 0000000..20da106 Binary files /dev/null and b/stuff/doc/DensityIno.pdf differ diff --git a/stuff/doc/DivideIno.pdf b/stuff/doc/DivideIno.pdf new file mode 100644 index 0000000..6b00470 Binary files /dev/null and b/stuff/doc/DivideIno.pdf differ diff --git a/stuff/doc/FogIno.pdf b/stuff/doc/FogIno.pdf new file mode 100644 index 0000000..748f5cc Binary files /dev/null and b/stuff/doc/FogIno.pdf differ diff --git a/stuff/doc/HLSAddIno.pdf b/stuff/doc/HLSAddIno.pdf new file mode 100644 index 0000000..47ce60a Binary files /dev/null and b/stuff/doc/HLSAddIno.pdf differ diff --git a/stuff/doc/HLSAdjustIno.pdf b/stuff/doc/HLSAdjustIno.pdf new file mode 100644 index 0000000..9ca423b Binary files /dev/null and b/stuff/doc/HLSAdjustIno.pdf differ diff --git a/stuff/doc/HLSNoiseIno.pdf b/stuff/doc/HLSNoiseIno.pdf new file mode 100644 index 0000000..5924856 Binary files /dev/null and b/stuff/doc/HLSNoiseIno.pdf differ diff --git a/stuff/doc/HSVAddIno.pdf b/stuff/doc/HSVAddIno.pdf new file mode 100644 index 0000000..0d64768 Binary files /dev/null and b/stuff/doc/HSVAddIno.pdf differ diff --git a/stuff/doc/HSVAdjustIno.pdf b/stuff/doc/HSVAdjustIno.pdf new file mode 100644 index 0000000..c174488 Binary files /dev/null and b/stuff/doc/HSVAdjustIno.pdf differ diff --git a/stuff/doc/HSVNoiseIno.pdf b/stuff/doc/HSVNoiseIno.pdf new file mode 100644 index 0000000..51e1a0b Binary files /dev/null and b/stuff/doc/HSVNoiseIno.pdf differ diff --git a/stuff/doc/HardLightIno.pdf b/stuff/doc/HardLightIno.pdf new file mode 100644 index 0000000..1cfcff1 Binary files /dev/null and b/stuff/doc/HardLightIno.pdf differ diff --git a/stuff/doc/HardMixIno.pdf b/stuff/doc/HardMixIno.pdf new file mode 100644 index 0000000..4cdde98 Binary files /dev/null and b/stuff/doc/HardMixIno.pdf differ diff --git a/stuff/doc/LevelAutoIno.pdf b/stuff/doc/LevelAutoIno.pdf new file mode 100644 index 0000000..6d5be50 Binary files /dev/null and b/stuff/doc/LevelAutoIno.pdf differ diff --git a/stuff/doc/LevelMasterIno.pdf b/stuff/doc/LevelMasterIno.pdf new file mode 100644 index 0000000..a6d2679 Binary files /dev/null and b/stuff/doc/LevelMasterIno.pdf differ diff --git a/stuff/doc/LevelRGBAIno.pdf b/stuff/doc/LevelRGBAIno.pdf new file mode 100644 index 0000000..a84c3b9 Binary files /dev/null and b/stuff/doc/LevelRGBAIno.pdf differ diff --git a/stuff/doc/LightenIno.pdf b/stuff/doc/LightenIno.pdf new file mode 100644 index 0000000..112cac7 Binary files /dev/null and b/stuff/doc/LightenIno.pdf differ diff --git a/stuff/doc/LighterColorIno.pdf b/stuff/doc/LighterColorIno.pdf new file mode 100644 index 0000000..bcfce35 Binary files /dev/null and b/stuff/doc/LighterColorIno.pdf differ diff --git a/stuff/doc/LinearBurnIno.pdf b/stuff/doc/LinearBurnIno.pdf new file mode 100644 index 0000000..32958d3 Binary files /dev/null and b/stuff/doc/LinearBurnIno.pdf differ diff --git a/stuff/doc/LinearDodgeIno.pdf b/stuff/doc/LinearDodgeIno.pdf new file mode 100644 index 0000000..f6b603b Binary files /dev/null and b/stuff/doc/LinearDodgeIno.pdf differ diff --git a/stuff/doc/LinearLightIno.pdf b/stuff/doc/LinearLightIno.pdf new file mode 100644 index 0000000..cad01fb Binary files /dev/null and b/stuff/doc/LinearLightIno.pdf differ diff --git a/stuff/doc/MaxMinIno.pdf b/stuff/doc/MaxMinIno.pdf new file mode 100644 index 0000000..1e696ac Binary files /dev/null and b/stuff/doc/MaxMinIno.pdf differ diff --git a/stuff/doc/MedianIno.pdf b/stuff/doc/MedianIno.pdf new file mode 100644 index 0000000..85b32fe Binary files /dev/null and b/stuff/doc/MedianIno.pdf differ diff --git a/stuff/doc/MotionBlurIno.pdf b/stuff/doc/MotionBlurIno.pdf new file mode 100644 index 0000000..bd6c836 Binary files /dev/null and b/stuff/doc/MotionBlurIno.pdf differ diff --git a/stuff/doc/MotionBlurIwa.html b/stuff/doc/MotionBlurIwa.html new file mode 100644 index 0000000..ca2d4d9 --- /dev/null +++ b/stuff/doc/MotionBlurIwa.html @@ -0,0 +1,43 @@ + + + + MotionBlurFx Iwa + + +

MotionBlurFx Iwa

+ +

● 概要

+モーションブラーを生成するためのエフェクトです。
+「現在のフレームの前後の、シャッターを開放している時間」を指定すると、
+その間のオブジェクトの軌跡に合わせてブラーをかけます。
+iwa_BokehFxと同様、RGB値を露光値に変換してからブラーをかけますので、
+ハイライトの部分はぼかしても明るさを損なうことがありません。 + +

● 入力ポート

+
    +
  • Source : 入力画像を接続します。
  • +
+

● パラメータ

+ +
    +
  • Reference Object, Index : 動きを追うオブジェクトを指定します。
    +"Own Motion"に指定している場合は、入力画像のカラムの動きを追跡します。
  • +
  • Shutter Start : 現フレームのどれくらい前からシャッターを開放するかを指定します。
    +単位はフレームです。値が大きいほど、大きくブラーがかかります。
  • +
  • Start Value : シャッター開放時のブラーフィルタの強度を指定します。
  • +
  • Start Curve : シャッター開放時から現フレームまでのブラーのかかりかたを指定します。
    +デフォルト値は1で、ブラーフィルタはリニアに変化します。

  • +
  • Shutter End : 現フレームのどれくらい後にシャッターを閉じるかを指定します。
    +単位はフレームです。値が大きいほど、大きくブラーがかかります。
  • +
  • End Value : シャッター閉鎖時のブラーフィルタの強度を指定します。
  • +
  • End Curve : 現フレームからシャッター閉鎖時までのブラーのかかりかたを指定します。
    +デフォルト値は1で、ブラーフィルタはリニアに変化します。

  • +
  • Trace Resolution : シャッターの開いている間の、オブジェクトの軌跡をを追跡する細かさを指定します。
    +この値が1だと、曲線で移動しているオブジェクトでも、軌跡は直線になります。
  • +
  • Hardness : フィルムのガンマ値。RGB値と露光量の変換に用います。この値が大きいほど、ハイライトが強調されます。
  • +
  • Zanzo Mode : ONのとき、滑らかなブラーの代わりに、"Trace Resolution"の細かさで追跡した位置に、
    +飛び飛びに残像を描画します。
  • +
+ + + \ No newline at end of file diff --git a/stuff/doc/MotionWindIno.pdf b/stuff/doc/MotionWindIno.pdf new file mode 100644 index 0000000..91e0f27 Binary files /dev/null and b/stuff/doc/MotionWindIno.pdf differ diff --git a/stuff/doc/MultiplyIno.pdf b/stuff/doc/MultiplyIno.pdf new file mode 100644 index 0000000..c4a982d Binary files /dev/null and b/stuff/doc/MultiplyIno.pdf differ diff --git a/stuff/doc/NegateIno.pdf b/stuff/doc/NegateIno.pdf new file mode 100644 index 0000000..6d76bbd Binary files /dev/null and b/stuff/doc/NegateIno.pdf differ diff --git a/stuff/doc/OverIno.pdf b/stuff/doc/OverIno.pdf new file mode 100644 index 0000000..5e34291 Binary files /dev/null and b/stuff/doc/OverIno.pdf differ diff --git a/stuff/doc/OverlayIno.pdf b/stuff/doc/OverlayIno.pdf new file mode 100644 index 0000000..dfb1932 Binary files /dev/null and b/stuff/doc/OverlayIno.pdf differ diff --git a/stuff/doc/PNCloudsIno.pdf b/stuff/doc/PNCloudsIno.pdf new file mode 100644 index 0000000..20b5ad0 Binary files /dev/null and b/stuff/doc/PNCloudsIno.pdf differ diff --git a/stuff/doc/PNPerspectiveIwa.html b/stuff/doc/PNPerspectiveIwa.html new file mode 100644 index 0000000..7296f67 --- /dev/null +++ b/stuff/doc/PNPerspectiveIwa.html @@ -0,0 +1,41 @@ + + + + PN PerspectiveFx Iwa + + +

PN PerspectiveFx Iwa

+ +

● 概要

+水平面上に奥行きのあるPerlinノイズパターンを生成するためのエフェクトです。
+通常のノイズパターンだけではなく、inoWarpHVFxのオフセット参照画像として出力するモードや、
+ノイズパターンを水面の波の高さとしたとき、カメラから見た水面のフレネル反射強度を出力するモードを +選択することができます。 + +

● パラメータ

+
    +
  • Mode : 描画モード指定します。選べるモードは以下の4つです。
  • +
      +
    • Noise:Perlinノイズパターンを描画します。1ピクセルを100分割したサンプル点毎にノイズ値を計算します。
    • +
    • Noise (no resampled):Perlinノイズパターンを描画します。サブピクセル処理を行わないので高速に結果が得られますが、水平線近くのノイズパターンが不規則になります。
    • +
    • Warp HV offset:ノイズパターンを水面の高低としたとき、水面で反射した光が水平/垂直方向にどれくらいずれるかを、それぞれ赤チャンネル/緑チャンネルで表したものを描画します。Warp HV Fx Inoの"Hori","Vert"ポートに接続することで、水面の映りこみのゆがみをそれなりに再現することができます。後述の"Wave Height"パラメータによって、パターンの濃淡の度合いを調整できます。
      +※Warp HV Fx Inoのポートは、"Hori","Vert"とも赤チャンネルを参照するので、"Vert"ポート側では、Channel Selector Fx等で、チャンネルの変換が必要です。
    • +
    • Fresnel reflectivity:ノイズパターンを水面の高低としたとき、カメラから見た水面のフレネル反射強度を濃淡にして出力します。カメラ枠下辺での反射強度を明るさ0として正規化して表示されます。後述の"Wave Height"パラメータによって、パターンの濃淡の度合いを調整できます。
    • + +
    +
  • Size : 1代目のランダムパターンのサイズを指定します。
  • +
  • Evolution : 1代目のランダムパターンの展開を指定します。
  • +
  • Octaves : ランダムパターンを何代 合成するかを指定します。
  • +
  • Offset : 1代目のランダムパターンのオフセット位置を指定します。カメラ右方向がX軸に正、カメラ奥方向がY軸に正です。
  • +
  • p_Intensity : 現在の世代と、次の世代とのパターンの強度比を指定します。
  • +
  • p_Size : 現在の世代と、次の世代とのパターンのサイズ比を指定します。
  • +
  • p_Evolution : 現在の世代と、次の世代とのパターンの展開周期の比を指定します。
  • +
  • p_Offset : 現在の世代と、次の世代とのパターンのオフセット比を指定します。
  • +
  • Fov : カメラの縦方向の画角をdegreeで指定します。画角が大きいと、遠近が強調され(広角)、画角が小さいと、遠近が圧縮されたようになります(望遠)。
  • +
  • Eye Level : カメラの消失点(水平線)の位置を指定します。
  • +
  • Alpha Rendering : ノイズの強度値を、アルファチャンネルにも格納するかどうかを指定します。
  • +
  • Wave Height : ノイズパターンを水面の高低としたとき、ノイズの最大位置と最小位置との波の高さの差を指定します。"Warp HV offset" モードと "Fresnel reflectivity"モードでのみ有効です。
  • +
+ + + \ No newline at end of file diff --git a/stuff/doc/PerspectiveDistortIwa.html b/stuff/doc/PerspectiveDistortIwa.html new file mode 100644 index 0000000..f380d1b --- /dev/null +++ b/stuff/doc/PerspectiveDistortIwa.html @@ -0,0 +1,32 @@ + + + + Perspective Distort Fx Iwa + + +

Perspective Distort Fx Iwa

+ +

● 概要

+Follow時の地面の動き(手前が速く、奥が遅く動く=運動視差)を生成するためのエフェクトです。
+絵を左右に引き伸ばして台形に変形しますので、上下には歪みません。
+つまり、奥行きによる垂直方向の収縮はあらかじめ入力画像に盛り込まれている必要があります。
+また、並行移動する背景にこのエフェクトを適用する場合、背景の移動はSubXsheetの中にたたむ必要があります。

+ + +

● 入力ポート

+
    +
  • Source : 入力画像を接続します。
  • +
+ +

● パラメータ

+
    +
  • Vanishing Point : 変形時の中心となる点です。透視投影の場合は、消失点にあたります。
  • +
  • Anchor Point : 変形の基準となる垂直位置を決める点です。この点より上のピクセルは描画されません(透明になります)。 +また、この点を通る水平線上の絵は、サイズが変わりません。
  • +
  • Precision : 変形は拡大によってのみ行うので、解像度が落ちてしまいます。解像度の劣化を最大限緩和するために、 +あらかじめこの値の倍率だけ細かい解像度で入力画像を計算して変形します。「背景の取り込み解像度」を +「出力解像度」で割った値よりPrecisionを高くしても意味はありません。
  • +
+ + + \ No newline at end of file diff --git a/stuff/doc/PinLightIno.pdf b/stuff/doc/PinLightIno.pdf new file mode 100644 index 0000000..f1b7a4f Binary files /dev/null and b/stuff/doc/PinLightIno.pdf differ diff --git a/stuff/doc/RadialBlurIno.pdf b/stuff/doc/RadialBlurIno.pdf new file mode 100644 index 0000000..e6b9fa7 Binary files /dev/null and b/stuff/doc/RadialBlurIno.pdf differ diff --git a/stuff/doc/ScreenIno.pdf b/stuff/doc/ScreenIno.pdf new file mode 100644 index 0000000..b68ed69 Binary files /dev/null and b/stuff/doc/ScreenIno.pdf differ diff --git a/stuff/doc/SoftLightIno.pdf b/stuff/doc/SoftLightIno.pdf new file mode 100644 index 0000000..5e73bd3 Binary files /dev/null and b/stuff/doc/SoftLightIno.pdf differ diff --git a/stuff/doc/SpectrumIwa.html b/stuff/doc/SpectrumIwa.html new file mode 100644 index 0000000..14310e7 --- /dev/null +++ b/stuff/doc/SpectrumIwa.html @@ -0,0 +1,48 @@ + + + + SpectrumFx Iwa + + +

SpectrumFx Iwa

+ +

● 概要

+入力画像の輝度に応じて、薄膜干渉による虹色パターンを出力するエフェクトです。
+本当はグレアの虹色は薄膜干渉ではないのですが、グレアの素材としてもそれらしい結果を得ることができます。 + +

● 入力ポート

+
    +
  • Source : 入力画像を接続します。入力画像の輝度が、薄膜の厚さに対応します。
    +輝度が明るいほど膜厚は薄く、輝度0.0が後述の"Thickness Min"に、輝度1.0が"Thickness Max"にそれぞれ対応します。
  • +
  • Light : ライト画像を接続します。
    +ライト画像が接続されている場合、出力結果に以下の違いがあります。
  • +
      +
    • 出力結果のアルファチャンネルは、ライト画像のアルファチャンネルで置き換えられます。
    • +
    • 後述の"Light Threshold"パラメータが有効になり、ライト画像と虹色の画像をスクリーン合成して出力することができます。
    • +
    +
+ +

● パラメータ

+
    +
  • Intensity : 光の強度です。高いほど虹色画像が明るくなります。
  • +
  • Refractive Index : 媒質の屈折率です。高いほど虹色画像が明るくなります。
  • +
  • Thick(ness) Max : 薄膜の厚みの最大値です。入力画像の輝度0の部分の膜厚に相当します。
  • +
  • Thick(ness) Min : 薄膜の厚みの最小値です。入力画像の輝度1の部分の膜厚に相当します。

  • +※ Thicknessは負の値を取ることもできます。その場合、膜厚が負の部分はすべて0として計算されます。
    +※ ThicknessのMin,Maxは、値の大小を逆転することもできます。その場合、虹色のパターンが反転します。 +
  • R/G/B Gamma : 虹色パターンの、RGB各チャンネルのガンマ補正値です。

  • +
  • Lens Factor : 入力画像の輝度から虹色パターンを出力するときに追加できるガンマ補正値です。
    +これにより、例えば、同心円状のパターンなら、虹色の周期に凸/凹レンズを通したような歪みをつけることができます。
  • +
  • Light Threshold : ライト画像が接続されているとき、虹色画像にライト画像をスクリーン合成することができます。
    +Light Thresholdは、スクリーン合成をするのに必要なライト画像のアルファチャンネルの最小値です。
    +各ピクセルについて、以下のような処理が行われます。
  • +
      +
    • ライト画像のアルファチャンネルがLight Threshold以下のとき : スクリーン合成は行われません。
    • +
    • ライト画像のアルファチャンネルが1のとき : ライト画像が100%でスクリーン合成されます。
    • +
    • ライト画像のアルファチャンネルがLight Thresholdより大きく、1未満のとき : スクリーン合成の割合は線形補間されます。
    • +
    +Light Thresholdの値が小さいほど、ライト画像とのスクリーンの割合は大きくなるので、出力画像は明るくなります。負の値をとることもできます。 +
+ + + \ No newline at end of file diff --git a/stuff/doc/SpinBlurIno.pdf b/stuff/doc/SpinBlurIno.pdf new file mode 100644 index 0000000..05e6fc3 Binary files /dev/null and b/stuff/doc/SpinBlurIno.pdf differ diff --git a/stuff/doc/SubtractIno.pdf b/stuff/doc/SubtractIno.pdf new file mode 100644 index 0000000..588b7b6 Binary files /dev/null and b/stuff/doc/SubtractIno.pdf differ diff --git a/stuff/doc/TileIwa.html b/stuff/doc/TileIwa.html new file mode 100644 index 0000000..c69814e --- /dev/null +++ b/stuff/doc/TileIwa.html @@ -0,0 +1,45 @@ + + + + TileFx Iwa + + +

TileFx Iwa

+ +

● 概要

+入力画像をタイル状に並べるエフェクトです。
+Toonz既存の「TileFx」の機能を以下の点で強化したものです。 +
    +
  • 繰り返す範囲を、素材のバウンディングボックスに加え、カメラボックスにも指定できるようになりました。
  • +
  • 繰り返しの有無と枚数を、上下左右別々に指定できるようになりました。
  • +
  • 余白の幅を、水平方向、垂直方向別々に指定できるようになりました。
  • +
+ +

● 入力ポート

+
    +
  • Source : 入力画像を接続します。
  • +
+ +

● パラメータ

+
    +
  • Input Size : 繰り返す範囲を指定します。
    +"Bounding Box"  入力画像のバウンディングボックス内の領域を繰り返します。
    +"Camera Box"   カメラボックス内の領域を繰り返します。つまり、繰り返された画像はカメラ外に配置されます。 +

  • +
  • Left Quantity : 左側に繰り返す回数を指定します。
    +"No Tile"  繰り返しません。
    +"1 Tile"   1枚だけ繰り返します。
    +"Mutiple Tiles" 必要な範囲内で無限に繰り返します。 +
  • +
  • Right Quantity : 右側に繰り返す回数を指定します。(Left Quantityを参照)
  • +
  • Mirror Horizontally : ONのとき、水平方向に並ぶタイルが、1枚おきに左右反転されるようになります。
  • +
  • Horizontal Margin : タイルの水平方向のマージンです(単位 Unit)。負の値を持つことができます。
  • +
  • Top Quantity : 上側に繰り返す回数を指定します。(Left Quantityを参照)
  • +
  • Bottom Quantity : 下側に繰り返す回数を指定します。(Left Quantityを参照)
  • +
  • Mirror Vertically : ONのとき、垂直方向に並ぶタイルが、1枚おきに上下反転されるようになります。
  • +
  • Vertical Margin : タイルの垂直方向のマージンです(単位 Unit)。負の値を持つことができます。
  • +
+ + + + \ No newline at end of file diff --git a/stuff/doc/VividLightIno.pdf b/stuff/doc/VividLightIno.pdf new file mode 100644 index 0000000..a64795d Binary files /dev/null and b/stuff/doc/VividLightIno.pdf differ diff --git a/stuff/doc/WarpHVIno.pdf b/stuff/doc/WarpHVIno.pdf new file mode 100644 index 0000000..509ecab Binary files /dev/null and b/stuff/doc/WarpHVIno.pdf differ diff --git a/stuff/doc/img/Thumbs.db b/stuff/doc/img/Thumbs.db new file mode 100644 index 0000000..6083fa0 Binary files /dev/null and b/stuff/doc/img/Thumbs.db differ diff --git a/stuff/doc/img/fx_iwa_motionblur.png b/stuff/doc/img/fx_iwa_motionblur.png new file mode 100644 index 0000000..d40e53b Binary files /dev/null and b/stuff/doc/img/fx_iwa_motionblur.png differ diff --git a/stuff/doc/img/fx_iwa_perspective_distort.png b/stuff/doc/img/fx_iwa_perspective_distort.png new file mode 100644 index 0000000..2cbfb79 Binary files /dev/null and b/stuff/doc/img/fx_iwa_perspective_distort.png differ diff --git a/stuff/doc/img/fx_iwa_pn_perspective.png b/stuff/doc/img/fx_iwa_pn_perspective.png new file mode 100644 index 0000000..cafa019 Binary files /dev/null and b/stuff/doc/img/fx_iwa_pn_perspective.png differ diff --git a/stuff/doc/img/fx_iwa_spectrum.png b/stuff/doc/img/fx_iwa_spectrum.png new file mode 100644 index 0000000..9f196b3 Binary files /dev/null and b/stuff/doc/img/fx_iwa_spectrum.png differ diff --git a/stuff/doc/img/fx_iwa_tile.png b/stuff/doc/img/fx_iwa_tile.png new file mode 100644 index 0000000..e35933e Binary files /dev/null and b/stuff/doc/img/fx_iwa_tile.png differ diff --git a/stuff/doc/img/motionblur.png b/stuff/doc/img/motionblur.png new file mode 100644 index 0000000..2f78030 Binary files /dev/null and b/stuff/doc/img/motionblur.png differ diff --git a/stuff/doc/img/perspective_distort.png b/stuff/doc/img/perspective_distort.png new file mode 100644 index 0000000..001069f Binary files /dev/null and b/stuff/doc/img/perspective_distort.png differ diff --git a/stuff/doc/particlesFx.html b/stuff/doc/particlesFx.html new file mode 100644 index 0000000..28ddffd --- /dev/null +++ b/stuff/doc/particlesFx.html @@ -0,0 +1,1031 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParticlesFx�̃p�����[�^�ꗗ
PageCategoryParam NameDescriptionControl Image
SourceSource(Generator + Image)���͉摜�̕s�����x��Threshold�ȏ�̃G���A�ɁA�ϓ��Ƀp�[�e�B�N���𐶐�����B�Y���t���[���ɓ��͉摜�̃Z���������A���͑S�ʕs�����x��Threshold�����̏ꍇ�͓��͉摜�������̂Ɠ������ʂɂȂ�B��
�@�@Threshold��L�����ɗp�����邵�����l�i0-255�j�@
�@�@Multiple + Generators in Control Image���������ň͂܂ꂽ�s���������i�ȉ��u�������v�j����������ꍇ�ɉe������B
+ ON�̂Ƃ��F ��������p�[�e�B�N���̗ʂ��e���������Ƃɋϓ��ɂȂ�B���Ȃ킿�A�����������قǃp�[�e�B�N���̔������x���Z���Ȃ�B
+ OFF�̂Ƃ��F��������p�[�e�B�N���̖��x�͑S�Ă̕s���������ɂ‚��ċϓ��ɂȂ�B���Ȃ킿�A�����������قǃp�[�e�B�N���̗ʂ͌���B
�\
�@�@Use Control + Image Gradation���͉摜�̕s�����x�ɔ�Ⴕ�ăp�[�e�B�N���̔������x��ς���B���̃I�v�V�����͏��Multiple Generators in + Control Image�Ɠ����ɂ͎g�p�ł��Ȃ��B�\
�@�@Center X���͉摜�̖����ꍇ�́A�p�[�e�B�N�������G���A�̒��S�ʒu��X���W�B�\
�@�@Center Y���͉摜�̖����ꍇ�́A�p�[�e�B�N�������G���A�̒��S�ʒu��Y���W�B�\
�@�@Width���͉摜�̖����ꍇ�́A�p�[�e�B�N�������G���A�i�l�p�`�j�̕��B�\
�@�@Height���͉摜�̖����ꍇ�́A�p�[�e�B�N�������G���A�i�l�p�`�j�̍����B�\
�@Particle GenerationStarting + Frame�p�[�e�B�N�����������n�߂�t���[�������B�V�[���̃t���[������B���̒l������B�\
�@�@Birth Rate���̃t���[���Ŕ�������p�[�e�B�N�����\
�@�@Animation + StepParticle�̃A�j���[�V������Step�l�����ɂ���B������Step�l��2�ȏ�ɂ���ƁAParticle��Animation�Ŏw�肵���G�̕ω���Step�͐ݒ肵���l�̓��ɂȂ�B�\
�@�@Random Seed����Fx�̂ŗp�����郉���_���p�����[�^�̃V�[�h�l�\
�@ParticleAnimationHold Frame: + Source�J�����iLevel�ł͂Ȃ��j�̊e�Z�����烉���_���Ɍ��肷��J�n�t���[���̂܂܌Œ�B
+ Random Frame: �t���[���������_���ŕω�����B��LAnimationStep�l�̓��̎����B
+ Column: Source�J�����ɓ����Ă���Z���̏��ɕω�����B�ŏ��̃t���[������X�^�[�g�B
+ Column - Random Start: Source�J�����ɓ����Ă���Z���̏��ɕω�����B�����_���ȃt���[������X�^�[�g�B
+ Column Swing - Random Start: + Source�J�����ɓ����Ă���Z���̏��ɕω����A�Ō�܂ł�������t�Đ�����B�����_���ȃt���[������X�^�[�g�B
�\
PageCategoryParam NameDescriptionControl Image
Birth ParamsSpeedSpeed�p�[�e�B�N���̏����x��
�@�@Linked to + Scale�p�[�e�B�N���̃X�s�[�h�̑召���T�C�Y�̑召�ƃ����N������B���s��������悤�Ɍ�����悤�ɁA�傫���قǑ����Ȃ�B���܂茵���ɔ��֌W�ɂ͂Ȃ��悤���B�\
�@�@Speed Angle�p�[�e�B�N���̏����̌����B�O���������A90���E������
�@�@Use + Gradient AngleON�̂Ƃ��ASpeed Angle��ControlImage�̌��z�x�N�g���ɏ��������킹��B�@
�@Size,Mass& OrientationSize�T�C�Y�̍ő�^�ŏ��͈̔͂��w�肷��BControlImage���L��ꍇ�́A���̋P�x�������Ƃ���قǑ傫���Ȃ�BControlImage�������ꍇ�̓����_���B��
�@�@Perspective + DistributionON�̂Ƃ��A���ASize��ControlImage�����݂���ꍇ�A���q�̖��x���T�C�Y�ɔ���Ⴓ���Ĕz�u����B�C�ʂ̔g�̂悤�ɁA���s���̂��闱�q�̔z�u�ɗp����B���̃I�v�V������ON�̂Ƃ��AUse + Control Image Gradation�I�v�V�����͖��������B�@
�@�@Use Control + Image for the Whole LifetimeON�̂Ƃ��A�e�t���[�����ɂ��̃p�[�e�B�N���̈ʒu��ControlImage�̋P�x�ɍ��킹�ăT�C�Y��ω�������B
+ OFF�̂Ƃ��A�p�[�e�B�N���������̂�ControlImage������B�ȍ~�͏�����܂œ����T�C�Y�B
�\
�@�@Mass�e�p�[�e�B�N���̎��ʂ̍ő�^�ŏ��͈̔͂��w�肷��B ���ʂ͉���Gravity�ŗ^������X�s�[�h�Ɋւ��W���ɂȂ�B�\
�@�@Orientation�p�[�e�B�N���̏����̌����̊p�x�̍ő�^�ŏ��͈̔͂��w�肷��B�O�ʼn�]�Ȃ��A90�ʼnE��90�x��]�B ��
�@TrailTrail�O�Ղ�\��������BTrail�Ŏw�肵���t���[���������ăt�F�[�h�A�E�g����B�\
�@�@Step�O�Ղ�Step�l�����ɕ\������B���Ȃ킿�A�O�Ղ̉摜�́iTrail�j/(Frame)���\������邱�ƂɂȂ�B�\
�@LifetimeLifetime�p�[�e�B�N���̎���(�t���[����)�̍ő�^�ŏ��͈̔͂��w�肷��B�����̓p�[�e�B�N��������n�߂Ă��犮�S�ɏ�����܂ł̃t���[�����B��
�@�@Use Column + Duration for LifetimeON�̂Ƃ��A�p�[�e�B�N���̎�����SourceColumn�̒����ɂȂ�B�\
�@Top LayerTop Layer�p�[�e�B�N���̏d�ˏ��̗D�揇�ʂ��w�肷��B�\
PageCategoryParam NameDescriptionControl Image
EnvironmentGravityGravity�p�[�e�B�N���ɗ^����d�́i�������x�j�̒l���w�肷��B���̒l�ŋt�����ɂȂ�BControlImage����͂����ꍇ�͉���GravityAngle�̒l�͗p����ꂸ�A���͉摜�̋P�x�̌��z�iGradient�j���d�͂ƂȂ�B���邢���Ɉ����񂹂���B��
�@�@Gravity + Angle�d�͂̕������w�肷��B�O�ʼn������B90�ō������ɗ����Ă����B�\
�@FrictionFriction�p�[�e�B�N���̉^�����~�߂�悤�Ȗ��C�̗͂̒l���w�肷��B���͉摜�̋P�x���ő�l�̕����ōő�̖��C�͂ƂȂ�B���͉摜��������Ζ��������B��
�@WindWind + Intensity�p�[�e�B�N������l�ɓ��������̑������w�肷��B�����x�ł͂Ȃ��A���x�ɑ������B�v�͏����x�iBirth + Param��Speed�j�Ɠ����Ӗ��Ǝv����B�\
�@�@Wind Angle���̌������w�肷��B0�Ő^��A90�ʼnE�����B�\
�@ScatteringHorizontal�p�[�e�B�N���̃����_���ȓ����̃p�����[�^���w�肷��B�p�[�e�B�N���Ƀ����_���ɓ�����^���鐅�������̃X�s�[�h�l�͈̔͂��w�肷��B��
�@�@Vertical�p�[�e�B�N���Ƀ����_���ɓ�����^���鐂�������̃X�s�[�h�l�͈̔͂��w�肷��B��
�@�@Swing ModeRandom�F + �e�t���[������Horizontal/Vertical�l��ς���B�������A����Swing�l�Ŏw�肳�ꂽ�t���[�����̊Ԃ́AHorizontal/Vertical�l�̐����̕����͕ς��Ȃ��B
+ Smooth�F ����Swing�l�Ŏw�肳�ꂽ�t���[�����̊ԁAHorizontal/Vertical�l�͕s�ρB
�\
�@�@SwingSwingMode��Smooth�̂Ƃ��AHorizontal/Vertical�l���Đݒ肷��t���[���Ԋu�̍ŏ�/�ő�l���w�肷��B�\
PageCategoryParam NameDescriptionControl Image
AnimationRotationRotation + Speed�e�p�[�e�B�N���̉�]�X�s�[�h���w�肷��B1�t���[��������̉�]�p�i�P��degree�j�\
�@�@Extra Speed�e�p�[�e�B�N���Ƀ����_���Œlj������p�x�͈̔͂��w�肷��B�\
�@�@Swing ModeRandom: �e�t���[������ExtraSpeed���v�Z���Ȃ����B
+ Smooth: ����RotationSwing�Ŏw�肳�ꂽ�t���[�����̊ԁA����ExtraSpeed�l�ʼn�]����B
�\
�@�@Rotation + SwingSwingMode��Smooth�̂Ƃ��AExtraSpeed�l���Đݒ肷��t���[���Ԋu�̍ŏ�/�ő�l���w�肷��B�\
�@�@Follow + Particles MovementON�̂Ƃ��A�p�[�e�B�N���̉�]�p��i�s�����ɉ��킹��B�E���������ɐi�ގ��A��]�p�͂O�B + ���̉�]�͏�L��Rotation�Əd�˂����ł���B�\
�@OpacityOpacity�p�[�e�B�N���̕s�����x�̍ŏ�/�ő�l�B�ŏ��l����ő�l�܂Ńt�F�[�h�C�����āA�ŏ��l�܂Ńt�F�[�h�A�E�g����悤�ɂȂ�B
+ + ControlImage����͂���ƁA���͉摜�̋P�x�l�ɉ����Ă��̃t���[�����_�ł̃p�[�e�B�N���̕s�����x�����܂�B�^�����̂Ƃ��ɍŏ��l�̕s�����x�ɂȂ�B�s�����x�͖��t���[���X�V�����B
+ ControlImage�ɂ��Opacity�́A���̃t�F�[�h�C��/�A�E�g�Əd�˂������邱�Ƃ��ł���B
��
�@�@Fade-in + Frames�t�F�[�h�C���ɂ�����t���[�����\
�@�@fade-out + Frames�t�F�[�h�A�E�g�ɂ�����t���[�����\
�@�@Trail + OpacityBirthParam��Trail��Trail���w�肵���Ƃ��A���̋O�Ղ̃p�[�e�B�N���̕s�����x�̍ŏ�/�ő�l�����߂�B�\
�@Size IncreaseSize Intensity�e�t���[�����Ƀ����_���ŕω�����T�C�Y�̑����l�i���j�̍ŏ�/�ő�l���w�肷��B��
PageCategoryParam NameDescriptionControl Image
ColorsBirth ColorBirth Color�p�[�e�B�N���������̐F�����肷��B
+ ���ꂼ��̃p�[�e�B�N�������������Ƃ��ɁA�X�y�N�g���Ŏw�肳�ꂽ�F�͈̔͂��烉���_���ɑI�΂��B
+ ControlImage���w�肵���ꍇ�ɂ́A���͉摜�́iPremultiply���ꂽ�jRGB�l���p�[�e�B�N���̐F�Ɏg�p�����
��
�@�@Birth + Spread�@�\
�@�@Birth + IntensityBirth Color�̉e���x�i���j�\
�@�@Pick Control Image's Color for Every Frame���t���[���A���݂̃p�[�e�B�N���̈ʒu�ɂ���ControlImage�̃s�N�Z�����Q�Ƃ��A�p�[�e�B�N���̐F��ς����\
�@Fade-in + ColorFade-in + Color�p�[�e�B�N�����t�F�[�h�C�����Ă����F�����肷��B
+ �X�y�N�g���Ŏw�肳�ꂽ�F�͈̔͂��烉���_���ɑI�΂��B
+ ControlImage���w�肵���ꍇ�ɂ́A���͉摜�́iPremultiply���ꂽ�jRGB�l���p�[�e�B�N���̐F�Ɏg�p�����
��
�@�@Fade-in + Spread�@�\
�@�@Frame Range�p�[�e�B�N���̔������琔����Fade-in Color�Ɏ���܂ł̃t���[�����\
�@�@Fade-in + IntensityFade-in Color�̉e���x�i���j�\
�@Fade-out + ColorFade-out + Color�p�[�e�B�N�����t�F�[�h�A�E�g���Ă����F�����肷��B
+ �X�y�N�g���Ŏw�肳�ꂽ�F�͈̔͂��烉���_���ɑI�΂��B
+ ControlImage���w�肵���ꍇ�ɂ́A���͉摜�́iPremultiply���ꂽ�jRGB�l���p�[�e�B�N���̐F�Ɏg�p�����
��
�@�@Fade-out + Spread�@�\
�@�@Frame Range�p�[�e�B�N���̏��ł���t�ɐ�����Fade-out Color�֕ω����n�߂�t���[�����\
�@�@Fade-out + IntensityFade-out Color�̉e���x�i���j�\
+ +
+ + + + + + + + diff --git a/stuff/profiles/layouts/check.bmp b/stuff/profiles/layouts/check.bmp new file mode 100644 index 0000000..3f9b05b Binary files /dev/null and b/stuff/profiles/layouts/check.bmp differ diff --git a/stuff/profiles/layouts/fxs/SHADER_caustics.xml b/stuff/profiles/layouts/fxs/SHADER_caustics.xml new file mode 100644 index 0000000..326cdf6 --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_caustics.xml @@ -0,0 +1,9 @@ + + + color + intensity + time + + + + diff --git a/stuff/profiles/layouts/fxs/SHADER_fireball.xml b/stuff/profiles/layouts/fxs/SHADER_fireball.xml new file mode 100644 index 0000000..985d61b --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_fireball.xml @@ -0,0 +1,8 @@ + + + color1 + color2 + detail + time + + diff --git a/stuff/profiles/layouts/fxs/SHADER_glitter.xml b/stuff/profiles/layouts/fxs/SHADER_glitter.xml new file mode 100644 index 0000000..5773523 --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_glitter.xml @@ -0,0 +1,9 @@ + + + threshold + brightness + radius + angle + halo + + diff --git a/stuff/profiles/layouts/fxs/SHADER_radialblurGPU.xml b/stuff/profiles/layouts/fxs/SHADER_radialblurGPU.xml new file mode 100644 index 0000000..ccbecdb --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_radialblurGPU.xml @@ -0,0 +1,7 @@ + + + center + radius + blur + + diff --git a/stuff/profiles/layouts/fxs/SHADER_spinblurGPU.xml b/stuff/profiles/layouts/fxs/SHADER_spinblurGPU.xml new file mode 100644 index 0000000..362b01c --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_spinblurGPU.xml @@ -0,0 +1,7 @@ + + + center + radius + blur + + diff --git a/stuff/profiles/layouts/fxs/SHADER_starsky.xml b/stuff/profiles/layouts/fxs/SHADER_starsky.xml new file mode 100644 index 0000000..bc11f1d --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_starsky.xml @@ -0,0 +1,7 @@ + + + color + time + brightness + + diff --git a/stuff/profiles/layouts/fxs/SHADER_sunflare.xml b/stuff/profiles/layouts/fxs/SHADER_sunflare.xml new file mode 100644 index 0000000..3c8f987 --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_sunflare.xml @@ -0,0 +1,10 @@ + + + color + blades + intensity + angle + bias + sharpness + + diff --git a/stuff/profiles/layouts/fxs/SHADER_wavy.xml b/stuff/profiles/layouts/fxs/SHADER_wavy.xml new file mode 100644 index 0000000..4fee60e --- /dev/null +++ b/stuff/profiles/layouts/fxs/SHADER_wavy.xml @@ -0,0 +1,9 @@ + + + color1 + color2 + time + + + + diff --git a/stuff/profiles/layouts/fxs/STD_adjustLevelsFx.xml b/stuff/profiles/layouts/fxs/STD_adjustLevelsFx.xml new file mode 100644 index 0000000..b9d99c5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_adjustLevelsFx.xml @@ -0,0 +1,28 @@ + + + + in_rgb + out_rgb + + in_r + out_r + + in_g + out_g + + in_b + out_b + + in_m + out_m + + + + gamma_rgb + gamma_r + gamma_g + gamma_b + gamma_m + + + diff --git a/stuff/profiles/layouts/fxs/STD_artContourFx.xml b/stuff/profiles/layouts/fxs/STD_artContourFx.xml new file mode 100644 index 0000000..85cdd09 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_artContourFx.xml @@ -0,0 +1,17 @@ + + + + Color_Index + Keep_color + Keep_Line + Include_Alpha + + + Density + Distance + Size + Orientation + Randomness + + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_backlitFx.xml b/stuff/profiles/layouts/fxs/STD_backlitFx.xml new file mode 100644 index 0000000..3260d57 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_backlitFx.xml @@ -0,0 +1,8 @@ + + + value + color + fade + + + diff --git a/stuff/profiles/layouts/fxs/STD_blendTzFx.xml b/stuff/profiles/layouts/fxs/STD_blendTzFx.xml new file mode 100644 index 0000000..99bce68 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_blendTzFx.xml @@ -0,0 +1,11 @@ + + + + Color_Index + Amount + Smoothness + noBlending + + + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_blurFx.xml b/stuff/profiles/layouts/fxs/STD_blurFx.xml new file mode 100644 index 0000000..44815c1 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_blurFx.xml @@ -0,0 +1,7 @@ + + + value + spread + + + diff --git a/stuff/profiles/layouts/fxs/STD_bodyHighLightFx.xml b/stuff/profiles/layouts/fxs/STD_bodyHighLightFx.xml new file mode 100644 index 0000000..c60f1fe --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_bodyHighLightFx.xml @@ -0,0 +1,11 @@ + + + mode + point + transparency + blur + color + invert + + + diff --git a/stuff/profiles/layouts/fxs/STD_brightContFx.xml b/stuff/profiles/layouts/fxs/STD_brightContFx.xml new file mode 100644 index 0000000..9069bbc --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_brightContFx.xml @@ -0,0 +1,7 @@ + + + brightness + contrast + + + diff --git a/stuff/profiles/layouts/fxs/STD_calligraphicFx.xml b/stuff/profiles/layouts/fxs/STD_calligraphicFx.xml new file mode 100644 index 0000000..87436e8 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_calligraphicFx.xml @@ -0,0 +1,19 @@ + + + Color_Index + Thickness + + Accuracy + Noise + + + + Horizontal + Vertical + upWDiagonal + doWDiagonal + + + + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_castShadowFx.xml b/stuff/profiles/layouts/fxs/STD_castShadowFx.xml new file mode 100644 index 0000000..8fadb1e --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_castShadowFx.xml @@ -0,0 +1,24 @@ + + + distort_type + + bottom_left_b + bottom_right_b + top_left_b + top_right_b + + bottom_left_a + bottom_right_a + top_left_a + top_right_a + deactivate + color + fade + up_transp + down_transp + up_blur + down_blur + + + + diff --git a/stuff/profiles/layouts/fxs/STD_channelMixerFx.xml b/stuff/profiles/layouts/fxs/STD_channelMixerFx.xml new file mode 100644 index 0000000..4f5292a --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_channelMixerFx.xml @@ -0,0 +1,21 @@ + + + red_to_red + green_to_red + blue_to_red + matte_to_red + red_to_green + green_to_green + blue_to_green + matte_to_green + red_to_blue + green_to_blue + blue_to_blue + matte_to_blue + red_to_matte + green_to_matte + blue_to_matte + matte_to_matte + + + diff --git a/stuff/profiles/layouts/fxs/STD_cloudsFx.xml b/stuff/profiles/layouts/fxs/STD_cloudsFx.xml new file mode 100644 index 0000000..6d602e9 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_cloudsFx.xml @@ -0,0 +1,11 @@ + + + type + size + evolution + colors + min + max + + + diff --git a/stuff/profiles/layouts/fxs/STD_colorEmbossFx.xml b/stuff/profiles/layouts/fxs/STD_colorEmbossFx.xml new file mode 100644 index 0000000..b2e7a26 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_colorEmbossFx.xml @@ -0,0 +1,9 @@ + + + intensity + elevation + direction + radius + + + diff --git a/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml b/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml new file mode 100644 index 0000000..b103cff --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml @@ -0,0 +1,11 @@ + + + p + z + intensity + decay + smoothness + includeInput + + + diff --git a/stuff/profiles/layouts/fxs/STD_cornerPinFx.xml b/stuff/profiles/layouts/fxs/STD_cornerPinFx.xml new file mode 100644 index 0000000..406afe2 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_cornerPinFx.xml @@ -0,0 +1,22 @@ + + + distort_type + + bottom_left_b + bottom_right_b + top_left_b + top_right_b + + bottom_left_a + bottom_right_a + top_left_a + top_right_a + deactivate + + + indexes + mode + keep + value + + diff --git a/stuff/profiles/layouts/fxs/STD_despeckleFx.xml b/stuff/profiles/layouts/fxs/STD_despeckleFx.xml new file mode 100644 index 0000000..e48d9f2 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_despeckleFx.xml @@ -0,0 +1,8 @@ + + + size + detect_speckles_on + + + + diff --git a/stuff/profiles/layouts/fxs/STD_diamondGradientFx.xml b/stuff/profiles/layouts/fxs/STD_diamondGradientFx.xml new file mode 100644 index 0000000..2d87510 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_diamondGradientFx.xml @@ -0,0 +1,7 @@ + + + colors + size + + + diff --git a/stuff/profiles/layouts/fxs/STD_directionalBlurFx.xml b/stuff/profiles/layouts/fxs/STD_directionalBlurFx.xml new file mode 100644 index 0000000..7cd6966 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_directionalBlurFx.xml @@ -0,0 +1,10 @@ + + + angle + intensity + bidirectional + spread + + + + diff --git a/stuff/profiles/layouts/fxs/STD_dissolveFx.xml b/stuff/profiles/layouts/fxs/STD_dissolveFx.xml new file mode 100644 index 0000000..ab1e80d --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_dissolveFx.xml @@ -0,0 +1,6 @@ + + + intensity + + + diff --git a/stuff/profiles/layouts/fxs/STD_embossFx.xml b/stuff/profiles/layouts/fxs/STD_embossFx.xml new file mode 100644 index 0000000..b58737e --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_embossFx.xml @@ -0,0 +1,10 @@ + + + intensity + elevation + direction + radius + + + + diff --git a/stuff/profiles/layouts/fxs/STD_erodeDilateFx.xml b/stuff/profiles/layouts/fxs/STD_erodeDilateFx.xml new file mode 100644 index 0000000..944ca4d --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_erodeDilateFx.xml @@ -0,0 +1,8 @@ + + + radius + type + + + + diff --git a/stuff/profiles/layouts/fxs/STD_fadeFx.xml b/stuff/profiles/layouts/fxs/STD_fadeFx.xml new file mode 100644 index 0000000..295b2d9 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_fadeFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_fourPointsGradientFx.xml b/stuff/profiles/layouts/fxs/STD_fourPointsGradientFx.xml new file mode 100644 index 0000000..260e821 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_fourPointsGradientFx.xml @@ -0,0 +1,14 @@ + + + Point_1 + Color_1 + Point_2 + Color_2 + Point_3 + Color_3 + Point_4 + Color_4 + + + + diff --git a/stuff/profiles/layouts/fxs/STD_freeDistortFx.xml b/stuff/profiles/layouts/fxs/STD_freeDistortFx.xml new file mode 100644 index 0000000..ac3f7ef --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_freeDistortFx.xml @@ -0,0 +1,18 @@ + + + distort_type + + bottom_left_b + bottom_right_b + top_left_b + top_right_b + + bottom_left_a + bottom_right_a + top_left_a + top_right_a + deactivate + + + + diff --git a/stuff/profiles/layouts/fxs/STD_gammaFx.xml b/stuff/profiles/layouts/fxs/STD_gammaFx.xml new file mode 100644 index 0000000..d5c8d14 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_gammaFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_glowFx.xml b/stuff/profiles/layouts/fxs/STD_glowFx.xml new file mode 100644 index 0000000..4a6d50f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_glowFx.xml @@ -0,0 +1,9 @@ + + + value + brightness + color + fade + + + diff --git a/stuff/profiles/layouts/fxs/STD_hsvKeyFx.xml b/stuff/profiles/layouts/fxs/STD_hsvKeyFx.xml new file mode 100644 index 0000000..dac7030 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_hsvKeyFx.xml @@ -0,0 +1,13 @@ + + + h + s + v + h_range + s_range + v_range + invert + + + + diff --git a/stuff/profiles/layouts/fxs/STD_hsvScaleFx.xml b/stuff/profiles/layouts/fxs/STD_hsvScaleFx.xml new file mode 100644 index 0000000..6fb1416 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_hsvScaleFx.xml @@ -0,0 +1,11 @@ + + + hue + saturation + value + hue_scale + saturation_scale + value_scale + + + diff --git a/stuff/profiles/layouts/fxs/STD_inoAddFx.xml b/stuff/profiles/layouts/fxs/STD_inoAddFx.xml new file mode 100644 index 0000000..3ff6047 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoAddFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoBlurFx.xml new file mode 100644 index 0000000..4a4e328 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoBlurFx.xml @@ -0,0 +1,7 @@ + + + radius + + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoChannelSelectorFx.xml b/stuff/profiles/layouts/fxs/STD_inoChannelSelectorFx.xml new file mode 100644 index 0000000..d045450 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoChannelSelectorFx.xml @@ -0,0 +1,16 @@ + + + + red_source + red_channel + + green_source + green_channel + + blue_source + blue_channel + + alpha_source + alpha_channel + + diff --git a/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml b/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml new file mode 100644 index 0000000..4e061e8 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml b/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml new file mode 100644 index 0000000..808776b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml b/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml new file mode 100644 index 0000000..fa04bd3 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml b/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml new file mode 100644 index 0000000..4d9555b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml b/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml new file mode 100644 index 0000000..3d28fe5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoDensityFx.xml b/stuff/profiles/layouts/fxs/STD_inoDensityFx.xml new file mode 100644 index 0000000..3c6972b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoDensityFx.xml @@ -0,0 +1,7 @@ + + + density + + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml b/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml new file mode 100644 index 0000000..e52dc55 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoFogFx.xml b/stuff/profiles/layouts/fxs/STD_inoFogFx.xml new file mode 100644 index 0000000..a5073ed --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoFogFx.xml @@ -0,0 +1,10 @@ + + + radius + curve + power + threshold_min + threshold_max + alpha_rendering + + diff --git a/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml new file mode 100644 index 0000000..0c624e7 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml b/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml new file mode 100644 index 0000000..30e073c --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLevelAutoFx.xml b/stuff/profiles/layouts/fxs/STD_inoLevelAutoFx.xml new file mode 100644 index 0000000..ac9ea01 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLevelAutoFx.xml @@ -0,0 +1,9 @@ + + + in_min_shift + in_max_shift + out_min + out_max + gamma + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLevelMasterFx.xml b/stuff/profiles/layouts/fxs/STD_inoLevelMasterFx.xml new file mode 100644 index 0000000..2dfa11b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLevelMasterFx.xml @@ -0,0 +1,11 @@ + + + in + out + gamma + alpha_rendering + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLevelrgbaFx.xml b/stuff/profiles/layouts/fxs/STD_inoLevelrgbaFx.xml new file mode 100644 index 0000000..c1a6f2a --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLevelrgbaFx.xml @@ -0,0 +1,23 @@ + + + + red_in + red_out + red_gamma + + gre_in + gre_out + gre_gamma + + blu_in + blu_out + blu_gamma + + alp_in + alp_out + alp_gamma + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml b/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml new file mode 100644 index 0000000..f8ccb1f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml b/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml new file mode 100644 index 0000000..56d6903 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLineBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoLineBlurFx.xml new file mode 100644 index 0000000..178bf27 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLineBlurFx.xml @@ -0,0 +1,18 @@ + + + action_mode + + blur_count + blur_power + blur_subpixel + blur_near_ref + blur_near_len + + vector_smooth_retry + vector_near_ref + vector_near_len + + smudge_thick + smudge_remain + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml new file mode 100644 index 0000000..54b4cec --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml new file mode 100644 index 0000000..8f6d1e9 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml new file mode 100644 index 0000000..ff37116 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMaxMinFx.xml b/stuff/profiles/layouts/fxs/STD_inoMaxMinFx.xml new file mode 100644 index 0000000..874de0b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMaxMinFx.xml @@ -0,0 +1,11 @@ + + + max_min_select + radius + polygon_number + degree + alpha_rendering + + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMedianFilterFx.xml b/stuff/profiles/layouts/fxs/STD_inoMedianFilterFx.xml new file mode 100644 index 0000000..d932fb5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMedianFilterFx.xml @@ -0,0 +1,8 @@ + + + radius + channel + + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMedianFx.xml b/stuff/profiles/layouts/fxs/STD_inoMedianFx.xml new file mode 100644 index 0000000..d4580a3 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMedianFx.xml @@ -0,0 +1,8 @@ + + + radius + channel + + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMotionBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoMotionBlurFx.xml new file mode 100644 index 0000000..799de2d --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMotionBlurFx.xml @@ -0,0 +1,14 @@ + + + depend_move + x1 + y1 + x2 + y2 + scale + curve + zanzo_length + zanzo_power + alpha_rendering + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMotionWindFx.xml b/stuff/profiles/layouts/fxs/STD_inoMotionWindFx.xml new file mode 100644 index 0000000..d12547e --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMotionWindFx.xml @@ -0,0 +1,24 @@ + + + direction + dark + alpha_rendering + length_min + length_max + length_bias + length_seed + force_min + force_max + force_bias + force_seed + density_min + density_max + density_bias + density_seed + + length_ref + force_ref + density_ref + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml b/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml new file mode 100644 index 0000000..bee7925 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoNegateFx.xml b/stuff/profiles/layouts/fxs/STD_inoNegateFx.xml new file mode 100644 index 0000000..31113f2 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoNegateFx.xml @@ -0,0 +1,8 @@ + + + red + green + blue + alpha + + diff --git a/stuff/profiles/layouts/fxs/STD_inoOverFx.xml b/stuff/profiles/layouts/fxs/STD_inoOverFx.xml new file mode 100644 index 0000000..5d2de36 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoOverFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml b/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml new file mode 100644 index 0000000..b44ce29 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml new file mode 100644 index 0000000..ce8953d --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml new file mode 100644 index 0000000..eace9eb --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml @@ -0,0 +1,12 @@ + + + center + radius + blur + twist + alpha_rendering + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml b/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml new file mode 100644 index 0000000..8543c3a --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml new file mode 100644 index 0000000..8ece638 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml new file mode 100644 index 0000000..0340739 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml @@ -0,0 +1,12 @@ + + + center + radius + blur + type + alpha_rendering + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml b/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml new file mode 100644 index 0000000..8c3c075 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml @@ -0,0 +1,7 @@ + + + opacity + clipping_mask + alpha_rendering + + diff --git a/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml new file mode 100644 index 0000000..c226972 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml @@ -0,0 +1,6 @@ + + + opacity + clipping_mask + + diff --git a/stuff/profiles/layouts/fxs/STD_inoWarphvFx.xml b/stuff/profiles/layouts/fxs/STD_inoWarphvFx.xml new file mode 100644 index 0000000..35afc68 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inoWarphvFx.xml @@ -0,0 +1,10 @@ + + + h_ref_mode + h_maxlen + v_ref_mode + v_maxlen + alpha_rendering + anti_aliasing + + diff --git a/stuff/profiles/layouts/fxs/STD_inohlsAddFx.xml b/stuff/profiles/layouts/fxs/STD_inohlsAddFx.xml new file mode 100644 index 0000000..a4959c6 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohlsAddFx.xml @@ -0,0 +1,13 @@ + + + from_rgba + offset + hue + lightness + saturation + alpha + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inohlsAdjustFx.xml b/stuff/profiles/layouts/fxs/STD_inohlsAdjustFx.xml new file mode 100644 index 0000000..2fdfbb8 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohlsAdjustFx.xml @@ -0,0 +1,19 @@ + + + + pivot_hue + pivot_lightness + pivot_saturation + + scale_hue + scale_lightness + scale_saturation + + shift_hue + shift_lightness + shift_saturation + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inohlsNoiseFx.xml b/stuff/profiles/layouts/fxs/STD_inohlsNoiseFx.xml new file mode 100644 index 0000000..b850ff8 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohlsNoiseFx.xml @@ -0,0 +1,17 @@ + + + hue + lightness + saturation + alpha + seed + nblur + + effective + center + type + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inohsvAddFx.xml b/stuff/profiles/layouts/fxs/STD_inohsvAddFx.xml new file mode 100644 index 0000000..a8a0605 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohsvAddFx.xml @@ -0,0 +1,13 @@ + + + from_rgba + offset + hue + saturation + value + alpha + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inohsvAdjustFx.xml b/stuff/profiles/layouts/fxs/STD_inohsvAdjustFx.xml new file mode 100644 index 0000000..f75b3e1 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohsvAdjustFx.xml @@ -0,0 +1,19 @@ + + + + pivot_hue + pivot_saturation + pivot_value + + scale_hue + scale_saturation + scale_value + + shift_hue + shift_saturation + shift_value + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inohsvNoiseFx.xml b/stuff/profiles/layouts/fxs/STD_inohsvNoiseFx.xml new file mode 100644 index 0000000..f4e88c9 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inohsvNoiseFx.xml @@ -0,0 +1,17 @@ + + + hue + saturation + value + alpha + seed + nblur + + effective + center + type + + anti_alias + reference + + diff --git a/stuff/profiles/layouts/fxs/STD_inopnCloudsFx.xml b/stuff/profiles/layouts/fxs/STD_inopnCloudsFx.xml new file mode 100644 index 0000000..1ba181f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_inopnCloudsFx.xml @@ -0,0 +1,9 @@ + + + size + z + octaves + persistance + alpha_rendering + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml new file mode 100644 index 0000000..71170ef --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml @@ -0,0 +1,7 @@ + + + hardness + scale + offset + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_DirectionalBlurFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_DirectionalBlurFx.xml new file mode 100644 index 0000000..f3644dc --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_DirectionalBlurFx.xml @@ -0,0 +1,8 @@ + + + angle + intensity + bidirectional + filterType + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml new file mode 100644 index 0000000..629917a --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml @@ -0,0 +1,7 @@ + + + h_maxlen + v_maxlen + scale + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml new file mode 100644 index 0000000..a76618c --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml @@ -0,0 +1,20 @@ + + + + + motionObjectType + motionObjectIndex + + shutterStart + startValue + startCurve + shutterEnd + endValue + endCurve + traceResolution + hardness + zanzoMode + premultiType + + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_PNPerspectiveFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_PNPerspectiveFx.xml new file mode 100644 index 0000000..77d43ce --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_PNPerspectiveFx.xml @@ -0,0 +1,18 @@ + + + renderMode + noiseType + size + evolution + octaves + offset + persistance_intensity + persistance_size + persistance_evolution + persistance_offset + fov + eyeLevel + alpha_rendering + waveHeight + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_PerspectiveDistortFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_PerspectiveDistortFx.xml new file mode 100644 index 0000000..77cdfbb --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_PerspectiveDistortFx.xml @@ -0,0 +1,7 @@ + + + vanishingPoint + anchorPoint + precision + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml new file mode 100644 index 0000000..4fbd999 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml @@ -0,0 +1,16 @@ + + + + intensity + refractiveIndex + thickMax + thickMin + RGamma + GGamma + BGamma + lensFactor + lightThres + lightIntensity + + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_TileFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_TileFx.xml new file mode 100644 index 0000000..5eb0468 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_TileFx.xml @@ -0,0 +1,17 @@ + + + inputSize + + + leftQuantity + rightQuantity + xMirror + hMargin + + + topQuantity + bottomQuantity + yMirror + vMargin + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_iwa_TiledParticlesFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_TiledParticlesFx.xml new file mode 100644 index 0000000..d120b01 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_TiledParticlesFx.xml @@ -0,0 +1,176 @@ + + + + rendermode + + + + base_ctrl + margin + + curl + + curl_ctrl + curl_ctrl_2 + + triangleSize + + flap_ctrl + flap_velocity + flap_dir_sensitivity + + + light_theta + light_phi + + + + source_ctrl + bright_thres + + source_gradation + center + + length + height + + + + + + + starting_frame + birth_rate + + + step + random_seed + + + + + + animation + + + + + + + speed + speed_ctrl + speed_size + + speed_angle + + speeda_ctrl + speeda_use_gradient + + + + + + scale + scale_ctrl + scale_ctrl_all + mass + rot + rot_ctrl + + trail + trail_step + + lifetime + lifetime_ctrl + column_lifetime + + top_layer + + + + + + + + + gravity + gravity_angle + + + gravity_ctrl + gravityBufferFrame + + + + friction + friction_ctrl + + + + wind + wind_angle + + + + + scattering_x + scattering_y + + scattering_x_ctrl + scattering_y_ctrl + + swing_mode + swing + + + + + + + spin_speed + spin_random + spin_swing_mode + spin_swing + path_aim + + + + opacity + opacity_ctrl + + fade_in + fade_out + + trail_opacity + + scale_step + scale_step_ctrl + + + + + birth_color + birth_color_ctrl + birth_color_spread + birth_color_fade + pick_color_for_every_frame + + fadein_color + fadein_color_ctrl + + fadein_color_spread + fadein_color_range + + fadein_color_fade + + fadeout_color + fadeout_color_ctrl + + fadeout_color_spread + fadeout_color_range + + fadeout_color_fade + + + + diff --git a/stuff/profiles/layouts/fxs/STD_kaleidoFx.xml b/stuff/profiles/layouts/fxs/STD_kaleidoFx.xml new file mode 100644 index 0000000..6f0eb2d --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_kaleidoFx.xml @@ -0,0 +1,9 @@ + + + center + angle + count + + + + diff --git a/stuff/profiles/layouts/fxs/STD_lightSpotFx.xml b/stuff/profiles/layouts/fxs/STD_lightSpotFx.xml new file mode 100644 index 0000000..8e59145 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_lightSpotFx.xml @@ -0,0 +1,9 @@ + + + softness + a + b + color + + + diff --git a/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml b/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml new file mode 100644 index 0000000..7eeebcb --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml @@ -0,0 +1,14 @@ + + + period + color1 + color2 + color1 color2 + + wave_amplitude + wave_frequency + wave_phase + + + + diff --git a/stuff/profiles/layouts/fxs/STD_linearWaveFx.xml b/stuff/profiles/layouts/fxs/STD_linearWaveFx.xml new file mode 100644 index 0000000..a84ade6 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_linearWaveFx.xml @@ -0,0 +1,19 @@ + + + + count + period + cycle + + amplitude + frequency + phase + + angle + + intensity + sensitivity + sharpen + + + diff --git a/stuff/profiles/layouts/fxs/STD_localBlurFx.xml b/stuff/profiles/layouts/fxs/STD_localBlurFx.xml new file mode 100644 index 0000000..cb979a5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_localBlurFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_localTransparencyFx.xml b/stuff/profiles/layouts/fxs/STD_localTransparencyFx.xml new file mode 100644 index 0000000..133f032 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_localTransparencyFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_mosaicFx.xml b/stuff/profiles/layouts/fxs/STD_mosaicFx.xml new file mode 100644 index 0000000..5faa24f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_mosaicFx.xml @@ -0,0 +1,10 @@ + + + size + distance + bg_color + shape + + + + diff --git a/stuff/profiles/layouts/fxs/STD_motionBlurFx.xml b/stuff/profiles/layouts/fxs/STD_motionBlurFx.xml new file mode 100644 index 0000000..311514f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_motionBlurFx.xml @@ -0,0 +1,6 @@ + + + intensity + + + diff --git a/stuff/profiles/layouts/fxs/STD_multiLinearGradientFx.xml b/stuff/profiles/layouts/fxs/STD_multiLinearGradientFx.xml new file mode 100644 index 0000000..988baef --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_multiLinearGradientFx.xml @@ -0,0 +1,14 @@ + + + period + count + cycle + colors + + wave_amplitude + wave_frequency + wave_phase + + + + diff --git a/stuff/profiles/layouts/fxs/STD_multiRadialGradientFx.xml b/stuff/profiles/layouts/fxs/STD_multiRadialGradientFx.xml new file mode 100644 index 0000000..738463a --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_multiRadialGradientFx.xml @@ -0,0 +1,9 @@ + + + period + count + cycle + colors + + + diff --git a/stuff/profiles/layouts/fxs/STD_multiToneFx.xml b/stuff/profiles/layouts/fxs/STD_multiToneFx.xml new file mode 100644 index 0000000..62e0df3 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_multiToneFx.xml @@ -0,0 +1,7 @@ + + + colors + + + + diff --git a/stuff/profiles/layouts/fxs/STD_noiseFx.xml b/stuff/profiles/layouts/fxs/STD_noiseFx.xml new file mode 100644 index 0000000..4f93a99 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_noiseFx.xml @@ -0,0 +1,11 @@ + + + Intensity + Red + Green + Blue + Black_White + Animate + + + diff --git a/stuff/profiles/layouts/fxs/STD_outBorderFx.xml b/stuff/profiles/layouts/fxs/STD_outBorderFx.xml new file mode 100644 index 0000000..1273f68 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_outBorderFx.xml @@ -0,0 +1,18 @@ + + + Thickness + + Accuracy + Noise + + + + Horizontal + Vertical + upWDiagonal + doWDiagonal + + + + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_paletteFilterFx.xml b/stuff/profiles/layouts/fxs/STD_paletteFilterFx.xml new file mode 100644 index 0000000..14f6d1f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_paletteFilterFx.xml @@ -0,0 +1,9 @@ + + + indexes + type + keep + + + + diff --git a/stuff/profiles/layouts/fxs/STD_particlesFx.xml b/stuff/profiles/layouts/fxs/STD_particlesFx.xml new file mode 100644 index 0000000..01972ba --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_particlesFx.xml @@ -0,0 +1,153 @@ + + + + + + source_ctrl + bright_thres + + multi_source + source_gradation + center + + length + height + + + + + + + starting_frame + birth_rate + + + step + random_seed + + + + + + animation + + + + + + + speed + speed_ctrl + speed_size + + speed_angle + + speeda_ctrl + speeda_use_gradient + + + + + + scale + perspective_distribution + scale_ctrl + scale_ctrl_all + mass + rot + rot_ctrl + + trail + trail_step + + lifetime + lifetime_ctrl + column_lifetime + + top_layer + + + + + + + + + gravity + gravity_angle + + gravity_ctrl + + + friction + friction_ctrl + + + + wind + wind_angle + + + + + scattering_x + scattering_y + + scattering_x_ctrl + scattering_y_ctrl + + swing_mode + swing + + + + + + + spin_speed + spin_random + spin_swing_mode + spin_swing + path_aim + + + + opacity + opacity_ctrl + + fade_in + fade_out + + trail_opacity + + scale_step + scale_step_ctrl + + + + + birth_color + birth_color_ctrl + birth_color_spread + birth_color_fade + pick_color_for_every_frame + + fadein_color + fadein_color_ctrl + + fadein_color_spread + fadein_color_range + + fadein_color_fade + + fadeout_color + fadeout_color_ctrl + + fadeout_color_spread + fadeout_color_range + + fadeout_color_fade + + + + diff --git a/stuff/profiles/layouts/fxs/STD_perlinNoiseFx.xml b/stuff/profiles/layouts/fxs/STD_perlinNoiseFx.xml new file mode 100644 index 0000000..7409a7c --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_perlinNoiseFx.xml @@ -0,0 +1,14 @@ + + + type + size + min + max + offsetx + offsety + evolution + intensity + matte + + + diff --git a/stuff/profiles/layouts/fxs/STD_posterizeFx.xml b/stuff/profiles/layouts/fxs/STD_posterizeFx.xml new file mode 100644 index 0000000..9ad1d4b --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_posterizeFx.xml @@ -0,0 +1,6 @@ + + + levels + + + diff --git a/stuff/profiles/layouts/fxs/STD_radialBlurFx.xml b/stuff/profiles/layouts/fxs/STD_radialBlurFx.xml new file mode 100644 index 0000000..ae939eb --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_radialBlurFx.xml @@ -0,0 +1,8 @@ + + + point + radius + blur + + + diff --git a/stuff/profiles/layouts/fxs/STD_radialGradientFx.xml b/stuff/profiles/layouts/fxs/STD_radialGradientFx.xml new file mode 100644 index 0000000..9a9a0d5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_radialGradientFx.xml @@ -0,0 +1,10 @@ + + + innerperiod + color1 + period + color2 + color1 color2 + + + diff --git a/stuff/profiles/layouts/fxs/STD_randomWaveFx.xml b/stuff/profiles/layouts/fxs/STD_randomWaveFx.xml new file mode 100644 index 0000000..15a055e --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_randomWaveFx.xml @@ -0,0 +1,13 @@ + + + + evolution + positionx + positiony + + intensity + sensitivity + sharpen + + + diff --git a/stuff/profiles/layouts/fxs/STD_raylitFx.xml b/stuff/profiles/layouts/fxs/STD_raylitFx.xml new file mode 100644 index 0000000..307e02f --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_raylitFx.xml @@ -0,0 +1,13 @@ + + + p + z + color + intensity + decay + smoothness + includeInput + invert + + + diff --git a/stuff/profiles/layouts/fxs/STD_rgbKeyFx.xml b/stuff/profiles/layouts/fxs/STD_rgbKeyFx.xml new file mode 100644 index 0000000..f2be68c --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rgbKeyFx.xml @@ -0,0 +1,11 @@ + + + color + r_range + g_range + b_range + invert + + + + diff --git a/stuff/profiles/layouts/fxs/STD_rgbmCutFx.xml b/stuff/profiles/layouts/fxs/STD_rgbmCutFx.xml new file mode 100644 index 0000000..da3f2ca --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rgbmCutFx.xml @@ -0,0 +1,10 @@ + + + r_range + g_range + b_range + m_range + + + + diff --git a/stuff/profiles/layouts/fxs/STD_rgbmFadeFx.xml b/stuff/profiles/layouts/fxs/STD_rgbmFadeFx.xml new file mode 100644 index 0000000..e317d41 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rgbmFadeFx.xml @@ -0,0 +1,8 @@ + + + color + intensity + + + + diff --git a/stuff/profiles/layouts/fxs/STD_rgbmScaleFx.xml b/stuff/profiles/layouts/fxs/STD_rgbmScaleFx.xml new file mode 100644 index 0000000..0d6a852 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rgbmScaleFx.xml @@ -0,0 +1,9 @@ + + + red + green + blue + matte + + + diff --git a/stuff/profiles/layouts/fxs/STD_rippleFx.xml b/stuff/profiles/layouts/fxs/STD_rippleFx.xml new file mode 100644 index 0000000..aa05557 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rippleFx.xml @@ -0,0 +1,19 @@ + + + + period + count + cycle + center + + scalex + scaley + + angle + + intensity + sensitivity + sharpen + + + diff --git a/stuff/profiles/layouts/fxs/STD_rotationalBlurFx.xml b/stuff/profiles/layouts/fxs/STD_rotationalBlurFx.xml new file mode 100644 index 0000000..833412e --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_rotationalBlurFx.xml @@ -0,0 +1,8 @@ + + + point + radius + blur + + + diff --git a/stuff/profiles/layouts/fxs/STD_saltpepperNoiseFx.xml b/stuff/profiles/layouts/fxs/STD_saltpepperNoiseFx.xml new file mode 100644 index 0000000..391a0eb --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_saltpepperNoiseFx.xml @@ -0,0 +1,7 @@ + + + Intensity + Animate + + + diff --git a/stuff/profiles/layouts/fxs/STD_sharpenFx.xml b/stuff/profiles/layouts/fxs/STD_sharpenFx.xml new file mode 100644 index 0000000..f1132c8 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_sharpenFx.xml @@ -0,0 +1,7 @@ + + + intensity + + + + diff --git a/stuff/profiles/layouts/fxs/STD_solarizeFx.xml b/stuff/profiles/layouts/fxs/STD_solarizeFx.xml new file mode 100644 index 0000000..25e3b78 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_solarizeFx.xml @@ -0,0 +1,7 @@ + + + maximum + peak_edge + + + diff --git a/stuff/profiles/layouts/fxs/STD_spiralFx.xml b/stuff/profiles/layouts/fxs/STD_spiralFx.xml new file mode 100644 index 0000000..49a0571 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_spiralFx.xml @@ -0,0 +1,9 @@ + + + colors + freq + phase + + + + diff --git a/stuff/profiles/layouts/fxs/STD_squareGradientFx.xml b/stuff/profiles/layouts/fxs/STD_squareGradientFx.xml new file mode 100644 index 0000000..877b156 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_squareGradientFx.xml @@ -0,0 +1,7 @@ + + + colors + size + + + diff --git a/stuff/profiles/layouts/fxs/STD_targetSpotFx.xml b/stuff/profiles/layouts/fxs/STD_targetSpotFx.xml new file mode 100644 index 0000000..392f123 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_targetSpotFx.xml @@ -0,0 +1,11 @@ + + + z + angle + decay + sizeX + sizeY + color + + + diff --git a/stuff/profiles/layouts/fxs/STD_textureFx.xml b/stuff/profiles/layouts/fxs/STD_textureFx.xml new file mode 100644 index 0000000..9b09742 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_textureFx.xml @@ -0,0 +1,10 @@ + + + indexes + keep + mode + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_tileFx.xml b/stuff/profiles/layouts/fxs/STD_tileFx.xml new file mode 100644 index 0000000..4e1f460 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_tileFx.xml @@ -0,0 +1,10 @@ + + + mode + xMirror + yMirror + margin + + + + \ No newline at end of file diff --git a/stuff/profiles/layouts/fxs/STD_toneCurveFx.xml b/stuff/profiles/layouts/fxs/STD_toneCurveFx.xml new file mode 100644 index 0000000..0d106ad --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_toneCurveFx.xml @@ -0,0 +1,6 @@ + + + curve + + + diff --git a/stuff/profiles/layouts/fxs/STD_transparencyFx.xml b/stuff/profiles/layouts/fxs/STD_transparencyFx.xml new file mode 100644 index 0000000..252040c --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_transparencyFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/STD_warpFx.xml b/stuff/profiles/layouts/fxs/STD_warpFx.xml new file mode 100644 index 0000000..b0396d3 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_warpFx.xml @@ -0,0 +1,8 @@ + + + intensity + sensitivity + sharpen + + + diff --git a/stuff/profiles/layouts/fxs/addFx.xml b/stuff/profiles/layouts/fxs/addFx.xml new file mode 100644 index 0000000..32c06f1 --- /dev/null +++ b/stuff/profiles/layouts/fxs/addFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/blendFx.xml b/stuff/profiles/layouts/fxs/blendFx.xml new file mode 100644 index 0000000..966cc14 --- /dev/null +++ b/stuff/profiles/layouts/fxs/blendFx.xml @@ -0,0 +1,7 @@ + + + value + + + + diff --git a/stuff/profiles/layouts/fxs/checkboardFx.xml b/stuff/profiles/layouts/fxs/checkboardFx.xml new file mode 100644 index 0000000..33799e2 --- /dev/null +++ b/stuff/profiles/layouts/fxs/checkboardFx.xml @@ -0,0 +1,8 @@ + + + color1 + color2 + size + + + diff --git a/stuff/profiles/layouts/fxs/colorCardFx.xml b/stuff/profiles/layouts/fxs/colorCardFx.xml new file mode 100644 index 0000000..fc150f1 --- /dev/null +++ b/stuff/profiles/layouts/fxs/colorCardFx.xml @@ -0,0 +1,6 @@ + + + color + + + diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst new file mode 100644 index 0000000..509e17e --- /dev/null +++ b/stuff/profiles/layouts/fxs/fxs.lst @@ -0,0 +1,175 @@ + + + checkBoardFx + colorCardFx + STD_kaleidoFx + STD_tileFx + STD_iwa_TileFx + + + STD_blurFx + STD_inoBlurFx + STD_directionalBlurFx + STD_iwa_DirectionalBlurFx + STD_inoLineBlurFx + STD_localBlurFx + STD_motionBlurFx + STD_inoMotionBlurFx + STD_iwa_MotionBlurCompFx + STD_radialBlurFx + STD_inoRadialBlurFx + STD_rotationalBlurFx + STD_inoSpinBlurFx + + + STD_freeDistortFx + STD_iwa_GradientWarpFx + STD_linearWaveFx + STD_perlinNoiseFx + STD_iwa_PerspectiveDistortFx + STD_randomWaveFx + STD_rippleFx + STD_warpFx + STD_inoWarphvFx + + + STD_diamondGradientFx + STD_fourPointsGradientFx + STD_linearGradientFx + STD_multiLinearGradientFx + STD_multiRadialGradientFx + STD_radialGradientFx + STD_spiralFx + STD_squareGradientFx + + + STD_iwa_AdjustExposureFx + STD_adjustLevelsFx + STD_brightContFx + STD_channelMixerFx + STD_toneCurveFx + STD_despeckleFx + STD_inoChannelSelectorFx + STD_inoDensityFx + STD_gammaFx + STD_inohlsAddFx + STD_inohlsAdjustFx + STD_inohsvAddFx + STD_inohsvAdjustFx + STD_hsvScaleFx + invertFx + STD_inoLevelAutoFx + STD_inoLevelMasterFx + STD_inoLevelrgbaFx + STD_multiToneFx + STD_inoNegateFx + STD_rgbmCutFx + STD_rgbmFadeFx + STD_rgbmScaleFx + STD_sharpenFx + + + addFx + colorBurnFx + colorDodgeFx + blendFx + minFx + maxFx + STD_localTransparencyFx + multFx + overFx + STD_premultiplyFx + screenFx + subFx + STD_fadeFx + + + STD_inoOverFx + STD_inoCrossDissolveFx + STD_inoDarkenFx + STD_inoMultiplyFx + STD_inoColorBurnFx + STD_inoLinearBurnFx + STD_inoDarkerColorFx + STD_inoAddFx + STD_inoLightenFx + STD_inoScreenFx + STD_inoColorDodgeFx + STD_inoLinearDodgeFx + STD_inoLighterColorFx + STD_inoOverlayFx + STD_inoSoftLightFx + STD_inoHardLightFx + STD_inoVividLightFx + STD_inoLinearLightFx + STD_inoPinLightFx + STD_inoHardMixFx + STD_inoDivideFx + STD_inoSubtractFx + + + STD_backlitFx + STD_bodyHighLightFx + STD_castShadowFx + STD_colorRaylitFx + STD_inoFogFx + STD_glowFx + STD_lightSpotFx + STD_raylitFx + STD_iwa_SpectrumFx + STD_targetSpotFx + + + STD_erodeDilateFx + STD_hsvKeyFx + inFx + outFx + STD_rgbKeyFx + atopFx + + + STD_dissolveFx + STD_inohlsNoiseFx + STD_inohsvNoiseFx + STD_inoMedianFx + STD_inoMedianFilterFx + STD_noiseFx + STD_iwa_PNPerspectiveFx + STD_saltpepperNoiseFx + + + STD_cloudsFx + STD_inopnCloudsFx + STD_particlesFx + STD_iwa_TiledParticlesFx + + + STD_colorEmbossFx + STD_embossFx + STD_inoMaxMinFx + STD_mosaicFx + STD_inoMotionWindFx + STD_posterizeFx + STD_solarizeFx + + + STD_artContourFx + STD_calligraphicFx + STD_blendTzFx + STD_externalPaletteFx + STD_outBorderFx + STD_paletteFilterFx + STD_cornerPinFx + STD_textureFx + + + SHADER_caustics + SHADER_fireball + SHADER_glitter + SHADER_starsky + SHADER_sunflare + SHADER_wavy + SHADER_radialblurGPU + SHADER_spinblurGPU + + diff --git a/stuff/profiles/layouts/fxs/minFx.xml b/stuff/profiles/layouts/fxs/minFx.xml new file mode 100644 index 0000000..e9ca5ce --- /dev/null +++ b/stuff/profiles/layouts/fxs/minFx.xml @@ -0,0 +1,6 @@ + + + matte + + + diff --git a/stuff/profiles/layouts/fxs/multFx.xml b/stuff/profiles/layouts/fxs/multFx.xml new file mode 100644 index 0000000..ebcf1dc --- /dev/null +++ b/stuff/profiles/layouts/fxs/multFx.xml @@ -0,0 +1,7 @@ + + + value + matte + + + diff --git a/stuff/profiles/layouts/fxs/subFx.xml b/stuff/profiles/layouts/fxs/subFx.xml new file mode 100644 index 0000000..df695ba --- /dev/null +++ b/stuff/profiles/layouts/fxs/subFx.xml @@ -0,0 +1,7 @@ + + + matte + + + + diff --git a/stuff/profiles/layouts/menubar.xml b/stuff/profiles/layouts/menubar.xml new file mode 100644 index 0000000..3c2d93c --- /dev/null +++ b/stuff/profiles/layouts/menubar.xml @@ -0,0 +1,96 @@ + + + + M_File + + MI_NewScene + MI_LoadScene + MI_SaveScene + MI_SaveSceneAs + + MI_LoadLevel + MI_SaveLevel + MI_SaveLevelAs + + MI_Render + + MI_ShortcutPopup + + MI_Quit + + + + M_Edit + + MI_Undo + MI_Redo + + MI_Cut + MI_Copy + MI_Paste + + MI_Clear + MI_Insert + + + M_Level + + MI_AddFilmstripFramesPopup + MI_OpenRenumberPopup + MI_ConverttoVectors + + MI_MovetoScene + MI_MovetoFilmstrip + + MI_FileInfo + + MI_RemoveUnused + + M_Xsheet + + MI_SceneSettings + + MI_OpenChild + MI_CloseChild + MI_Collapse + MI_SaveSubxsheetAs + MI_Resequence + + M_Cells + + MI_Reverse + MI_Swing + MI_Random + + MI_Step2 + MI_Step3 + MI_Step4 + MI_Each2 + MI_Each3 + MI_Each4 + + MI_Duplicate + MI_CloneLevel + + M_View + + MI_ViewTable + MI_FieldGuide + MI_SafeArea + + MI_TCheck + + M_Windows + + MI_OpenFileViewer + MI_OpenPltView + MI_OpenFileBrowser2 + MI_OpenStyleControl + MI_OpenLevelView + MI_OpenXshView + + + + + + diff --git a/stuff/profiles/layouts/toolbars/maintoolbar.xml b/stuff/profiles/layouts/toolbars/maintoolbar.xml new file mode 100644 index 0000000..1b20107 --- /dev/null +++ b/stuff/profiles/layouts/toolbars/maintoolbar.xml @@ -0,0 +1,28 @@ + + T_Arrow + T_StrokeArrow + - + T_Pen + T_Primitive + T_Text + T_Fill + - + T_Eraser + T_Autoclose + - + T_PickStyle + T_PickRGBM + - + T_SplineEditor + T_ModifyStroke + T_ModifyThickness + T_Warp + T_Bender + T_Smoother + T_Cutter + - + T_Zoom + T_Rotate + T_Pan + + \ No newline at end of file diff --git a/stuff/profiles/project_folders.txt b/stuff/profiles/project_folders.txt new file mode 100644 index 0000000..322172f --- /dev/null +++ b/stuff/profiles/project_folders.txt @@ -0,0 +1,6 @@ +inputs +drawings +scenes +extras +outputs +palettes diff --git a/stuff/projects/cleanupreslist.txt b/stuff/projects/cleanupreslist.txt new file mode 100644 index 0000000..5ddea89 --- /dev/null +++ b/stuff/projects/cleanupreslist.txt @@ -0,0 +1,91 @@ +PAL analog, 768x576, 4/3 + +PAL digital, 720x576, 4/3 + +PAL 16/9, 1024x576, 16/9 + +NTSC digital, 720x486, 4/3 + +NTSC analog, 648x486, 4/3 + +NTSC 16/9, 864x486, 16/9 + +HD 1080, 1920x1080, 16/9 + +HD 720, 1280x720, 16/9 + +HD 480, 853x480, 16/9 + +Academy Full Frame1 1K, 914x666, 1.37 + +Academy Full Frame2 1K, 1024x746, 1.37 + +Academy Full Frame1 2K, 1828x1332, 1.37 + +Academy Full Frame2 2K, 2048x1494, 1.37 + +Academy Full Frame1 4K, 3656x2664, 1.37 + +Academy Full Frame2 4K, 4096x2988, 1.37 + +Full Camera Aperture 1K, 1024x768, 4/3 + +1K, 1024x768, 4/3 + +Full Camera Aperture 2K, 2048x1536, 4/3 + +2K, 2048x1536, 4/3 + +3K, 3072x2304, 4/3 + +Full Camera Aperture 4k, 4096x3072, 4/3 + +4K, 4096x3072, 4/3 + +Cineon Half Unsqueezed, 3675x1556, 2.36183 + +Cineon Half, 1828x1556, 2.35 + +Cineon Half Squeezed, 1828x1556, 2.36183 + +Cineon Full, 3656x3112, 2.35 + +Cineon Full, 4704x3112, 2.36183 + +Academy Projection1 1K, 914x550, 1.66 + +Academy Projection2 1K, 1024x617, 1.66 + +Academy Projection1 2K, 1828x1102, 1.66 + +Academy Projection2 2K, 2048x1229, 1.66 + +Academy Projection1 4K, 3656x2202, 1.66 + +Academy Projection2 4K, 4096x2468, 1.66 + +Academy Projection1 1K, 914x494, 1.85 + +Academy Projection2 1K, 1024x554, 1.85 + +Academy Projection1 2K, 1828x988, 1.85 + +Academy Projection2 2K, 2048x1107, 1.85 + +Academy Projection1 4K, 3656x1976, 1.85 + +Academy Projection2 4K, 4096x2214, 1.85 + +Anamorphic Pre-squeezed, 1K 914x774, 1.18 + +Anamorphic Pre-squeezed, 2K 1828x1550, 1.18 + +Anamorphic Pre-squeezed, 4K 3656x3098, 1.18 + +Anamorphic Un-squeezed, 1K 914x388, 2.35 + +Anamorphic Un-squeezed, 2K 1828x778, 2.35 + +Anamorphic Un-squeezed, 4K 3656x1556, 2.35 + + diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Bubbles.fx b/stuff/projects/fxs/presets/STD_particlesFx/Bubbles.fx new file mode 100644 index 0000000..d0a1146 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Bubbles.fx @@ -0,0 +1,755 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+ + + 0 + + + 1000 + + + + + 0 + + + 500 + + + + + 0 + + + 100 + + + 0 100 -1 0 1 0 1 + + + 1 0 -1 0 1 0 1 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 1 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 5 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 10 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + -10 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Falling leaves.fx b/stuff/projects/fxs/presets/STD_particlesFx/Falling leaves.fx new file mode 100644 index 0000000..cadf47c --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Falling leaves.fx @@ -0,0 +1,839 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 400 + + +
+ + + 0 + + + 700 + + + + + 0 + + + 40 + + + + + 0 + + + 5 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 -30 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 10 + + + + + 0 + + + 25 + + + + + + + 0 + + + -30 + + + + + 0 + + + 30 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 50 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 3 + + + 1 3 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Falling snow.fx b/stuff/projects/fxs/presets/STD_particlesFx/Falling snow.fx new file mode 100644 index 0000000..eba11a9 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Falling snow.fx @@ -0,0 +1,839 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 400 + + +
+ + + 0 + + + 1500 + + + + + 0 + + + 100 + + + + + 0 + + + 10 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 -100 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 1 + + + + + 0 + + + 3 + + + + + 0 + + + 5 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 5 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 30 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Fireworks.fx b/stuff/projects/fxs/presets/STD_particlesFx/Fireworks.fx new file mode 100644 index 0000000..e5fb5de --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Fireworks.fx @@ -0,0 +1,800 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + -164.361 + + + 0 -160.617 -1 0 1 0 1 + + + 9 112.652 -1 0 1 0 1 + + + + + 0 + + + 118.515 + + + 0 159.692 -1 0 1 0 1 + + + 9 118.515 -1 0 1 0 1 + + +
+ + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 30 + + + 0 30 -1 0 1 0 1 + + + 1 0 -1 0 1 0 1 + + + 8 0 -1 0 1 0 1 + + + 9 50 -1 0 1 0 1 + + + 10 0 -1 0 1 0 1 + + + + + + 0 + + + 20 + + + 0 20 -1 0 1 0 1 + + + + + 0 + + + 25 + + + 0 25 -1 0 1 0 1 + + + + + 1 0 + + + 1 1 + + + + 0 + + + 10 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 3 + + + 0 3 -1 0 1 0 1 + + + 9 -20 -1 0 1 0 1 + + + + + 0 + + + 10 + + + 0 10 -1 0 1 0 1 + + + 9 -6 -1 0 1 0 1 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 -35 -1 0 1 0 1 + + + + + 0 + + + 360 + + + 0 360 -1 0 1 0 1 + + + 9 35 -1 0 1 0 1 + + + + + 0 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 50 + + + 0 50 -1 0 1 0 1 + + + + + 0 + + + 50 + + + 0 100 -1 0 1 0 1 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 5 + + + 0 5 -1 0 1 0 1 + + + + + 0 + + + 10 + + + 0 10 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + + 0 + + + 0 + + + 0 1 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 1 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 0 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 100 + + + 0 100 -1 0 1 0 1 + + + 9 100 -1 0 1 0 1 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0.470588 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 5 + + + 0 15 -1 0 1 0 1 + + + 9 15 -1 0 1 0 1 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + 0 + + + 0.513725 + + + 0 0 -1 0 1 0 1 + + + 9 0.513725 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 9 0 -1 0 1 0 1 + + + + + 0 + + + 1 + + + 0 1 -1 0 1 0 1 + + + 9 1 -1 0 1 0 1 + + + + + + + -1 + + + + 0 + + + 5 + + + + + 0 + + + 0 + + + 0 100 -1 0 1 0 1 + + + 9 100 -1 0 1 0 1 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Grass.fx b/stuff/projects/fxs/presets/STD_particlesFx/Grass.fx new file mode 100644 index 0000000..0ec9df1 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Grass.fx @@ -0,0 +1,755 @@ + + + + 0 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+ + + 0 + + + 50 + + + + + 0 + + + 40 + + + + + 0 + + + 2000 + + + 0 2000 -1 0 1 0 1 + + + 1 0 -1 0 1 0 1 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 1 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + 3 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 5 + + + + + 0 + + + 100 + + + + + 0 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 3 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Rain.fx b/stuff/projects/fxs/presets/STD_particlesFx/Rain.fx new file mode 100644 index 0000000..7e80bea --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Rain.fx @@ -0,0 +1,749 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 300 + + +
+ + + 0 + + + 1500 + + + + + 0 + + + 40 + + + + + 0 + + + 30 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 -10 + + + 1 -10 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 100 + + + + + 0 + + + 150 + + + + + + + 0 + + + 30 + + + + + 0 + + + 32 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 50 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 270 + + + + + 0 + + + 270 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 1 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Smoke.fx b/stuff/projects/fxs/presets/STD_particlesFx/Smoke.fx new file mode 100644 index 0000000..75e9828 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Smoke.fx @@ -0,0 +1,764 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + -200 + + +
+ + + 0 + + + 100 + + + + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + + 0 + + + 10 + + + + + 0 + + + 20 + + + + + 1 -10 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 60 40 -1 0 1 0 1 + + + 67 0 -1 0 1 0 1 + + + + + 0 + + + 0 + + + 0 0 -1 0 1 0 1 + + + 60 -45 -1 0 1 0 1 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 11 + + + + + 0 + + + 21 + + + + + + + 0 + + + 160 + + + + + 0 + + + 210 + + + + + 0 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 60 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + -1 + + + + + 0 + + + -1 + + + + + + 0 + + + 3 + + + + + 0 + + + 14 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Spot smoke.fx b/stuff/projects/fxs/presets/STD_particlesFx/Spot smoke.fx new file mode 100644 index 0000000..61048ea --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Spot smoke.fx @@ -0,0 +1,623 @@ + + + + 0 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+ + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + 0 + + + 20 + + + + + + 0 + + + 50 + + + + + 0 + + + 70 + + + + + 1 1 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 5 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0.5 + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 3 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0.678431 + + + + + 0 + + + 0.678431 + + + + + 0 + + + 0.678431 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 5 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 30 + + + + + 0 + + + 100 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Starfield.fx b/stuff/projects/fxs/presets/STD_particlesFx/Starfield.fx new file mode 100644 index 0000000..35bf436 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Starfield.fx @@ -0,0 +1,1007 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+ + + 0 + + + 1000 + + + + + 0 + + + 500 + + + + + 0 + + + 10 + + + 0 100 -1 0 1 0 1 + + + 1 0 -1 0 1 0 1 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 1 + + + 1 5 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 50 + + + + + 0 + + + 100 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 5 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.402214 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.380073 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.442804 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.704797 + + + + + + 0 + + + 1 + + + + + 0 + + + 0.141176 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.734317 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 0.678966 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Steam.fx b/stuff/projects/fxs/presets/STD_particlesFx/Steam.fx new file mode 100644 index 0000000..3884032 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Steam.fx @@ -0,0 +1,749 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + 0 + + + + + 0 + + + -200 + + +
+ + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + 0 + + + 2 + + + + + + 0 + + + 20 + + + + + 0 + + + 40 + + + + + 1 -30 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 2 + + + + + 0 + + + 5 + + + + + + + 0 + + + 165 + + + + + 0 + + + 190 + + + + + 0 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 20 + + + + + 0 + + + 80 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 40 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + -1 + + + + + 0 + + + -1 + + + + + + 0 + + + 3 + + + + + 0 + + + 14 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Trail smoke.fx b/stuff/projects/fxs/presets/STD_particlesFx/Trail smoke.fx new file mode 100644 index 0000000..0834df1 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Trail smoke.fx @@ -0,0 +1,635 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + -280.264 + + + 0 -280.264 -1 0 1 0 1 + + + 99 280.26 -1 0 1 0 1 + + + + + 0 + + + -224.211 + + + 0 -224.211 -1 0 1 0 1 + + + 99 224.21 -1 0 1 0 1 + + +
+ + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + 0 + + + 20 + + + + + + 0 + + + 50 + + + + + 0 + + + 70 + + + + + 1 1 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 5 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + 0 1 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0.5 + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 3 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0.678431 + + + + + 0 + + + 0.678431 + + + + + 0 + + + 0.678431 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 5 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 30 + + + + + 0 + + + 100 + + +
+
diff --git a/stuff/projects/fxs/presets/STD_particlesFx/Water bubbles.fx b/stuff/projects/fxs/presets/STD_particlesFx/Water bubbles.fx new file mode 100644 index 0000000..8bbb6a3 --- /dev/null +++ b/stuff/projects/fxs/presets/STD_particlesFx/Water bubbles.fx @@ -0,0 +1,752 @@ + + + + -1 + + + 0 0 + +
+ + + 0 + + + -170.014 + + + + + 0 + + + -186.391 + + +
+ + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + 0 + + + 100 + + + 0 0.235 -1 0 1 0 1 + + + + + + 0 + + + 100 + + + + + 0 + + + 100 + + + + + 1 -100 + + + 1 1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 1 + + + + + 0 + + + 5 + + + + + 0 + + + 7 + + + + + + + 0 + + + 2 + + + + + 0 + + + 4 + + + + + + + 0 + + + 5 + + + + + 0 + + + 10 + + + + + + + 0 + + + 10 + + + + + 0 + + + 10 + + + + + + + 0 + + + 180 + + + + + 0 + + + 180 + + + + + 0 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + 0 + + + 2 + + + + + 0 + + + 15 + + + + + -1 + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 360 + + + + + -1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + 0 + + + + 0 + + + 0 + + + + + + 0 + + + -10 + + + + + 0 + + + 10 + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 0 + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0 + + + + + 0 + + + 100 + + + + + + + 0 + + + 0.2 + + + + + 0 + + + 0.5 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + 0 + + + 1 1 + + + + + + 0 + + + 0 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + 0 + + + 1 + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + + + + + -1 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + +
+
diff --git a/stuff/projects/library/custom styles/Arc.0001.png b/stuff/projects/library/custom styles/Arc.0001.png new file mode 100644 index 0000000..dac82fb Binary files /dev/null and b/stuff/projects/library/custom styles/Arc.0001.png differ diff --git a/stuff/projects/library/custom styles/Arc.0002.png b/stuff/projects/library/custom styles/Arc.0002.png new file mode 100644 index 0000000..cfb82f9 Binary files /dev/null and b/stuff/projects/library/custom styles/Arc.0002.png differ diff --git a/stuff/projects/library/custom styles/Arc.0003.png b/stuff/projects/library/custom styles/Arc.0003.png new file mode 100644 index 0000000..1194987 Binary files /dev/null and b/stuff/projects/library/custom styles/Arc.0003.png differ diff --git a/stuff/projects/library/custom styles/Ball.0001.png b/stuff/projects/library/custom styles/Ball.0001.png new file mode 100644 index 0000000..38cda31 Binary files /dev/null and b/stuff/projects/library/custom styles/Ball.0001.png differ diff --git a/stuff/projects/library/custom styles/Ball.0002.png b/stuff/projects/library/custom styles/Ball.0002.png new file mode 100644 index 0000000..cbfe34c Binary files /dev/null and b/stuff/projects/library/custom styles/Ball.0002.png differ diff --git a/stuff/projects/library/custom styles/Ball.0003.png b/stuff/projects/library/custom styles/Ball.0003.png new file mode 100644 index 0000000..184728a Binary files /dev/null and b/stuff/projects/library/custom styles/Ball.0003.png differ diff --git a/stuff/projects/library/custom styles/Bow.0001.png b/stuff/projects/library/custom styles/Bow.0001.png new file mode 100644 index 0000000..0fcad12 Binary files /dev/null and b/stuff/projects/library/custom styles/Bow.0001.png differ diff --git a/stuff/projects/library/custom styles/Bow.0002.png b/stuff/projects/library/custom styles/Bow.0002.png new file mode 100644 index 0000000..245fd85 Binary files /dev/null and b/stuff/projects/library/custom styles/Bow.0002.png differ diff --git a/stuff/projects/library/custom styles/Bow.0003.png b/stuff/projects/library/custom styles/Bow.0003.png new file mode 100644 index 0000000..19911e3 Binary files /dev/null and b/stuff/projects/library/custom styles/Bow.0003.png differ diff --git a/stuff/projects/library/custom styles/Brush.0001.png b/stuff/projects/library/custom styles/Brush.0001.png new file mode 100644 index 0000000..ea8bb8f Binary files /dev/null and b/stuff/projects/library/custom styles/Brush.0001.png differ diff --git a/stuff/projects/library/custom styles/Brush.0002.png b/stuff/projects/library/custom styles/Brush.0002.png new file mode 100644 index 0000000..52540b6 Binary files /dev/null and b/stuff/projects/library/custom styles/Brush.0002.png differ diff --git a/stuff/projects/library/custom styles/Brush.0003.png b/stuff/projects/library/custom styles/Brush.0003.png new file mode 100644 index 0000000..154c16c Binary files /dev/null and b/stuff/projects/library/custom styles/Brush.0003.png differ diff --git a/stuff/projects/library/custom styles/Brush2.0001.png b/stuff/projects/library/custom styles/Brush2.0001.png new file mode 100644 index 0000000..0722c7e Binary files /dev/null and b/stuff/projects/library/custom styles/Brush2.0001.png differ diff --git a/stuff/projects/library/custom styles/Brush2.0002.png b/stuff/projects/library/custom styles/Brush2.0002.png new file mode 100644 index 0000000..d61d55f Binary files /dev/null and b/stuff/projects/library/custom styles/Brush2.0002.png differ diff --git a/stuff/projects/library/custom styles/Brush2.0003.png b/stuff/projects/library/custom styles/Brush2.0003.png new file mode 100644 index 0000000..a9f178f Binary files /dev/null and b/stuff/projects/library/custom styles/Brush2.0003.png differ diff --git a/stuff/projects/library/custom styles/Brush2.0004.png b/stuff/projects/library/custom styles/Brush2.0004.png new file mode 100644 index 0000000..184939e Binary files /dev/null and b/stuff/projects/library/custom styles/Brush2.0004.png differ diff --git a/stuff/projects/library/custom styles/Candy.0001.png b/stuff/projects/library/custom styles/Candy.0001.png new file mode 100644 index 0000000..cdd6a96 Binary files /dev/null and b/stuff/projects/library/custom styles/Candy.0001.png differ diff --git a/stuff/projects/library/custom styles/Candy.0002.png b/stuff/projects/library/custom styles/Candy.0002.png new file mode 100644 index 0000000..f01ace3 Binary files /dev/null and b/stuff/projects/library/custom styles/Candy.0002.png differ diff --git a/stuff/projects/library/custom styles/Candy.0003.png b/stuff/projects/library/custom styles/Candy.0003.png new file mode 100644 index 0000000..7853150 Binary files /dev/null and b/stuff/projects/library/custom styles/Candy.0003.png differ diff --git a/stuff/projects/library/custom styles/Chick.0001.png b/stuff/projects/library/custom styles/Chick.0001.png new file mode 100644 index 0000000..15be3d3 Binary files /dev/null and b/stuff/projects/library/custom styles/Chick.0001.png differ diff --git a/stuff/projects/library/custom styles/Chick.0002.png b/stuff/projects/library/custom styles/Chick.0002.png new file mode 100644 index 0000000..f915c97 Binary files /dev/null and b/stuff/projects/library/custom styles/Chick.0002.png differ diff --git a/stuff/projects/library/custom styles/Chick.0003.png b/stuff/projects/library/custom styles/Chick.0003.png new file mode 100644 index 0000000..06a9886 Binary files /dev/null and b/stuff/projects/library/custom styles/Chick.0003.png differ diff --git a/stuff/projects/library/custom styles/Dog.0001.png b/stuff/projects/library/custom styles/Dog.0001.png new file mode 100644 index 0000000..771e2a5 Binary files /dev/null and b/stuff/projects/library/custom styles/Dog.0001.png differ diff --git a/stuff/projects/library/custom styles/Dog.0002.png b/stuff/projects/library/custom styles/Dog.0002.png new file mode 100644 index 0000000..7c33cc4 Binary files /dev/null and b/stuff/projects/library/custom styles/Dog.0002.png differ diff --git a/stuff/projects/library/custom styles/Dog.0003.png b/stuff/projects/library/custom styles/Dog.0003.png new file mode 100644 index 0000000..0f3125f Binary files /dev/null and b/stuff/projects/library/custom styles/Dog.0003.png differ diff --git a/stuff/projects/library/custom styles/Dog.0004.png b/stuff/projects/library/custom styles/Dog.0004.png new file mode 100644 index 0000000..0059a9f Binary files /dev/null and b/stuff/projects/library/custom styles/Dog.0004.png differ diff --git a/stuff/projects/library/custom styles/Fish2.0001.png b/stuff/projects/library/custom styles/Fish2.0001.png new file mode 100644 index 0000000..7d58331 Binary files /dev/null and b/stuff/projects/library/custom styles/Fish2.0001.png differ diff --git a/stuff/projects/library/custom styles/Fish2.0002.png b/stuff/projects/library/custom styles/Fish2.0002.png new file mode 100644 index 0000000..77eaaa6 Binary files /dev/null and b/stuff/projects/library/custom styles/Fish2.0002.png differ diff --git a/stuff/projects/library/custom styles/Fish2.0003.png b/stuff/projects/library/custom styles/Fish2.0003.png new file mode 100644 index 0000000..0c0a567 Binary files /dev/null and b/stuff/projects/library/custom styles/Fish2.0003.png differ diff --git a/stuff/projects/library/custom styles/Fish3.0001.png b/stuff/projects/library/custom styles/Fish3.0001.png new file mode 100644 index 0000000..221dd87 Binary files /dev/null and b/stuff/projects/library/custom styles/Fish3.0001.png differ diff --git a/stuff/projects/library/custom styles/Fish3.0002.png b/stuff/projects/library/custom styles/Fish3.0002.png new file mode 100644 index 0000000..8c708db Binary files /dev/null and b/stuff/projects/library/custom styles/Fish3.0002.png differ diff --git a/stuff/projects/library/custom styles/Fish3.0003.png b/stuff/projects/library/custom styles/Fish3.0003.png new file mode 100644 index 0000000..de8a6a0 Binary files /dev/null and b/stuff/projects/library/custom styles/Fish3.0003.png differ diff --git a/stuff/projects/library/custom styles/Fishbone.0001.png b/stuff/projects/library/custom styles/Fishbone.0001.png new file mode 100644 index 0000000..0523f44 Binary files /dev/null and b/stuff/projects/library/custom styles/Fishbone.0001.png differ diff --git a/stuff/projects/library/custom styles/Fishbone.0002.png b/stuff/projects/library/custom styles/Fishbone.0002.png new file mode 100644 index 0000000..44ec6b3 Binary files /dev/null and b/stuff/projects/library/custom styles/Fishbone.0002.png differ diff --git a/stuff/projects/library/custom styles/Fishbone.0003.png b/stuff/projects/library/custom styles/Fishbone.0003.png new file mode 100644 index 0000000..151234d Binary files /dev/null and b/stuff/projects/library/custom styles/Fishbone.0003.png differ diff --git a/stuff/projects/library/custom styles/Frame.0001.png b/stuff/projects/library/custom styles/Frame.0001.png new file mode 100644 index 0000000..eeb5eb6 Binary files /dev/null and b/stuff/projects/library/custom styles/Frame.0001.png differ diff --git a/stuff/projects/library/custom styles/Frame.0002.png b/stuff/projects/library/custom styles/Frame.0002.png new file mode 100644 index 0000000..a727166 Binary files /dev/null and b/stuff/projects/library/custom styles/Frame.0002.png differ diff --git a/stuff/projects/library/custom styles/Frame.0003.png b/stuff/projects/library/custom styles/Frame.0003.png new file mode 100644 index 0000000..6682a0a Binary files /dev/null and b/stuff/projects/library/custom styles/Frame.0003.png differ diff --git a/stuff/projects/library/custom styles/Fruit.0001.png b/stuff/projects/library/custom styles/Fruit.0001.png new file mode 100644 index 0000000..c9c1a45 Binary files /dev/null and b/stuff/projects/library/custom styles/Fruit.0001.png differ diff --git a/stuff/projects/library/custom styles/Fruit.0002.png b/stuff/projects/library/custom styles/Fruit.0002.png new file mode 100644 index 0000000..94c5c1f Binary files /dev/null and b/stuff/projects/library/custom styles/Fruit.0002.png differ diff --git a/stuff/projects/library/custom styles/Fruit.0003.png b/stuff/projects/library/custom styles/Fruit.0003.png new file mode 100644 index 0000000..30a5267 Binary files /dev/null and b/stuff/projects/library/custom styles/Fruit.0003.png differ diff --git a/stuff/projects/library/custom styles/Icecream.0001.png b/stuff/projects/library/custom styles/Icecream.0001.png new file mode 100644 index 0000000..6291498 Binary files /dev/null and b/stuff/projects/library/custom styles/Icecream.0001.png differ diff --git a/stuff/projects/library/custom styles/Icecream.0002.png b/stuff/projects/library/custom styles/Icecream.0002.png new file mode 100644 index 0000000..6bed524 Binary files /dev/null and b/stuff/projects/library/custom styles/Icecream.0002.png differ diff --git a/stuff/projects/library/custom styles/Icecream.0003.png b/stuff/projects/library/custom styles/Icecream.0003.png new file mode 100644 index 0000000..4f8afe1 Binary files /dev/null and b/stuff/projects/library/custom styles/Icecream.0003.png differ diff --git a/stuff/projects/library/custom styles/Icecream.0004.png b/stuff/projects/library/custom styles/Icecream.0004.png new file mode 100644 index 0000000..658dbf1 Binary files /dev/null and b/stuff/projects/library/custom styles/Icecream.0004.png differ diff --git a/stuff/projects/library/custom styles/Ladybird.0001.png b/stuff/projects/library/custom styles/Ladybird.0001.png new file mode 100644 index 0000000..01f767d Binary files /dev/null and b/stuff/projects/library/custom styles/Ladybird.0001.png differ diff --git a/stuff/projects/library/custom styles/Ladybird.0002.png b/stuff/projects/library/custom styles/Ladybird.0002.png new file mode 100644 index 0000000..10bded0 Binary files /dev/null and b/stuff/projects/library/custom styles/Ladybird.0002.png differ diff --git a/stuff/projects/library/custom styles/Ladybird.0003.png b/stuff/projects/library/custom styles/Ladybird.0003.png new file mode 100644 index 0000000..ed39582 Binary files /dev/null and b/stuff/projects/library/custom styles/Ladybird.0003.png differ diff --git a/stuff/projects/library/custom styles/Leaf2.0001.png b/stuff/projects/library/custom styles/Leaf2.0001.png new file mode 100644 index 0000000..69fa4bb Binary files /dev/null and b/stuff/projects/library/custom styles/Leaf2.0001.png differ diff --git a/stuff/projects/library/custom styles/Leaf2.0002.png b/stuff/projects/library/custom styles/Leaf2.0002.png new file mode 100644 index 0000000..172dec3 Binary files /dev/null and b/stuff/projects/library/custom styles/Leaf2.0002.png differ diff --git a/stuff/projects/library/custom styles/Leaf2.0003.png b/stuff/projects/library/custom styles/Leaf2.0003.png new file mode 100644 index 0000000..92c0c39 Binary files /dev/null and b/stuff/projects/library/custom styles/Leaf2.0003.png differ diff --git a/stuff/projects/library/custom styles/Leaf2.0004.png b/stuff/projects/library/custom styles/Leaf2.0004.png new file mode 100644 index 0000000..6b7773e Binary files /dev/null and b/stuff/projects/library/custom styles/Leaf2.0004.png differ diff --git a/stuff/projects/library/custom styles/Nail.0001.png b/stuff/projects/library/custom styles/Nail.0001.png new file mode 100644 index 0000000..089b004 Binary files /dev/null and b/stuff/projects/library/custom styles/Nail.0001.png differ diff --git a/stuff/projects/library/custom styles/Nail.0002.png b/stuff/projects/library/custom styles/Nail.0002.png new file mode 100644 index 0000000..a28bf0d Binary files /dev/null and b/stuff/projects/library/custom styles/Nail.0002.png differ diff --git a/stuff/projects/library/custom styles/Nail.0003.png b/stuff/projects/library/custom styles/Nail.0003.png new file mode 100644 index 0000000..70e21d0 Binary files /dev/null and b/stuff/projects/library/custom styles/Nail.0003.png differ diff --git a/stuff/projects/library/custom styles/Orange.0001.png b/stuff/projects/library/custom styles/Orange.0001.png new file mode 100644 index 0000000..672bdb5 Binary files /dev/null and b/stuff/projects/library/custom styles/Orange.0001.png differ diff --git a/stuff/projects/library/custom styles/Orange.0002.png b/stuff/projects/library/custom styles/Orange.0002.png new file mode 100644 index 0000000..222d810 Binary files /dev/null and b/stuff/projects/library/custom styles/Orange.0002.png differ diff --git a/stuff/projects/library/custom styles/Orange.0003.png b/stuff/projects/library/custom styles/Orange.0003.png new file mode 100644 index 0000000..76ba1cb Binary files /dev/null and b/stuff/projects/library/custom styles/Orange.0003.png differ diff --git a/stuff/projects/library/custom styles/Orange.0004.png b/stuff/projects/library/custom styles/Orange.0004.png new file mode 100644 index 0000000..0bf52d5 Binary files /dev/null and b/stuff/projects/library/custom styles/Orange.0004.png differ diff --git a/stuff/projects/library/custom styles/Pencil.0001.png b/stuff/projects/library/custom styles/Pencil.0001.png new file mode 100644 index 0000000..9c24d7d Binary files /dev/null and b/stuff/projects/library/custom styles/Pencil.0001.png differ diff --git a/stuff/projects/library/custom styles/Pencil.0002.png b/stuff/projects/library/custom styles/Pencil.0002.png new file mode 100644 index 0000000..848bdd7 Binary files /dev/null and b/stuff/projects/library/custom styles/Pencil.0002.png differ diff --git a/stuff/projects/library/custom styles/Pencil.0003.png b/stuff/projects/library/custom styles/Pencil.0003.png new file mode 100644 index 0000000..5aa2845 Binary files /dev/null and b/stuff/projects/library/custom styles/Pencil.0003.png differ diff --git a/stuff/projects/library/custom styles/Pencil.0004.png b/stuff/projects/library/custom styles/Pencil.0004.png new file mode 100644 index 0000000..a0d1d23 Binary files /dev/null and b/stuff/projects/library/custom styles/Pencil.0004.png differ diff --git a/stuff/projects/library/custom styles/Spring.0001.png b/stuff/projects/library/custom styles/Spring.0001.png new file mode 100644 index 0000000..b8f4dcf Binary files /dev/null and b/stuff/projects/library/custom styles/Spring.0001.png differ diff --git a/stuff/projects/library/custom styles/Spring.0002.png b/stuff/projects/library/custom styles/Spring.0002.png new file mode 100644 index 0000000..7fb5b30 Binary files /dev/null and b/stuff/projects/library/custom styles/Spring.0002.png differ diff --git a/stuff/projects/library/custom styles/Spring.0003.png b/stuff/projects/library/custom styles/Spring.0003.png new file mode 100644 index 0000000..13fe2c1 Binary files /dev/null and b/stuff/projects/library/custom styles/Spring.0003.png differ diff --git a/stuff/projects/library/custom styles/Spring.0004.png b/stuff/projects/library/custom styles/Spring.0004.png new file mode 100644 index 0000000..2ab2cd2 Binary files /dev/null and b/stuff/projects/library/custom styles/Spring.0004.png differ diff --git a/stuff/projects/library/custom styles/Sunflower.0001.png b/stuff/projects/library/custom styles/Sunflower.0001.png new file mode 100644 index 0000000..431cc43 Binary files /dev/null and b/stuff/projects/library/custom styles/Sunflower.0001.png differ diff --git a/stuff/projects/library/custom styles/Sunflower.0002.png b/stuff/projects/library/custom styles/Sunflower.0002.png new file mode 100644 index 0000000..552cf45 Binary files /dev/null and b/stuff/projects/library/custom styles/Sunflower.0002.png differ diff --git a/stuff/projects/library/custom styles/Sunflower.0003.png b/stuff/projects/library/custom styles/Sunflower.0003.png new file mode 100644 index 0000000..8e2ef45 Binary files /dev/null and b/stuff/projects/library/custom styles/Sunflower.0003.png differ diff --git a/stuff/projects/library/custom styles/Thumbs.db b/stuff/projects/library/custom styles/Thumbs.db new file mode 100644 index 0000000..b30123c Binary files /dev/null and b/stuff/projects/library/custom styles/Thumbs.db differ diff --git a/stuff/projects/library/custom styles/Umbrella.0001.png b/stuff/projects/library/custom styles/Umbrella.0001.png new file mode 100644 index 0000000..31d8bb4 Binary files /dev/null and b/stuff/projects/library/custom styles/Umbrella.0001.png differ diff --git a/stuff/projects/library/custom styles/Umbrella.0002.png b/stuff/projects/library/custom styles/Umbrella.0002.png new file mode 100644 index 0000000..16e1525 Binary files /dev/null and b/stuff/projects/library/custom styles/Umbrella.0002.png differ diff --git a/stuff/projects/library/custom styles/Umbrella.0003.png b/stuff/projects/library/custom styles/Umbrella.0003.png new file mode 100644 index 0000000..16cf4bf Binary files /dev/null and b/stuff/projects/library/custom styles/Umbrella.0003.png differ diff --git a/stuff/projects/library/custom styles/Umbrella.0004.png b/stuff/projects/library/custom styles/Umbrella.0004.png new file mode 100644 index 0000000..a095226 Binary files /dev/null and b/stuff/projects/library/custom styles/Umbrella.0004.png differ diff --git a/stuff/projects/library/custom styles/ant.pli b/stuff/projects/library/custom styles/ant.pli new file mode 100644 index 0000000..aca6059 Binary files /dev/null and b/stuff/projects/library/custom styles/ant.pli differ diff --git a/stuff/projects/library/custom styles/atta.0001.png b/stuff/projects/library/custom styles/atta.0001.png new file mode 100644 index 0000000..6b6e3c2 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0001.png differ diff --git a/stuff/projects/library/custom styles/atta.0002.png b/stuff/projects/library/custom styles/atta.0002.png new file mode 100644 index 0000000..000d514 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0002.png differ diff --git a/stuff/projects/library/custom styles/atta.0003.png b/stuff/projects/library/custom styles/atta.0003.png new file mode 100644 index 0000000..de80a43 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0003.png differ diff --git a/stuff/projects/library/custom styles/atta.0004.png b/stuff/projects/library/custom styles/atta.0004.png new file mode 100644 index 0000000..d799ce3 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0004.png differ diff --git a/stuff/projects/library/custom styles/atta.0005.png b/stuff/projects/library/custom styles/atta.0005.png new file mode 100644 index 0000000..e4576bc Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0005.png differ diff --git a/stuff/projects/library/custom styles/atta.0006.png b/stuff/projects/library/custom styles/atta.0006.png new file mode 100644 index 0000000..60e9ef9 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.0006.png differ diff --git a/stuff/projects/library/custom styles/atta.pli b/stuff/projects/library/custom styles/atta.pli new file mode 100644 index 0000000..a56dba9 Binary files /dev/null and b/stuff/projects/library/custom styles/atta.pli differ diff --git a/stuff/projects/library/custom styles/ball.pli b/stuff/projects/library/custom styles/ball.pli new file mode 100644 index 0000000..72ef730 Binary files /dev/null and b/stuff/projects/library/custom styles/ball.pli differ diff --git a/stuff/projects/library/custom styles/bell.pli b/stuff/projects/library/custom styles/bell.pli new file mode 100644 index 0000000..5f33e60 Binary files /dev/null and b/stuff/projects/library/custom styles/bell.pli differ diff --git a/stuff/projects/library/custom styles/big_.pli b/stuff/projects/library/custom styles/big_.pli new file mode 100644 index 0000000..0f7c885 Binary files /dev/null and b/stuff/projects/library/custom styles/big_.pli differ diff --git a/stuff/projects/library/custom styles/bubb.pli b/stuff/projects/library/custom styles/bubb.pli new file mode 100644 index 0000000..7dd0cb6 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb.pli differ diff --git a/stuff/projects/library/custom styles/bubb2.0001.png b/stuff/projects/library/custom styles/bubb2.0001.png new file mode 100644 index 0000000..2adb24f Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0001.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0002.png b/stuff/projects/library/custom styles/bubb2.0002.png new file mode 100644 index 0000000..3f39fb6 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0002.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0003.png b/stuff/projects/library/custom styles/bubb2.0003.png new file mode 100644 index 0000000..1282928 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0003.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0004.png b/stuff/projects/library/custom styles/bubb2.0004.png new file mode 100644 index 0000000..367b7e5 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0004.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0005.png b/stuff/projects/library/custom styles/bubb2.0005.png new file mode 100644 index 0000000..56d086e Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0005.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0006.png b/stuff/projects/library/custom styles/bubb2.0006.png new file mode 100644 index 0000000..8ce00b9 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0006.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0007.png b/stuff/projects/library/custom styles/bubb2.0007.png new file mode 100644 index 0000000..8754bde Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0007.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0008.png b/stuff/projects/library/custom styles/bubb2.0008.png new file mode 100644 index 0000000..3cc0af3 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0008.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0009.png b/stuff/projects/library/custom styles/bubb2.0009.png new file mode 100644 index 0000000..87412bf Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0009.png differ diff --git a/stuff/projects/library/custom styles/bubb2.0010.png b/stuff/projects/library/custom styles/bubb2.0010.png new file mode 100644 index 0000000..5032e60 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.0010.png differ diff --git a/stuff/projects/library/custom styles/bubb2.pli b/stuff/projects/library/custom styles/bubb2.pli new file mode 100644 index 0000000..4c3d131 Binary files /dev/null and b/stuff/projects/library/custom styles/bubb2.pli differ diff --git a/stuff/projects/library/custom styles/clou.pli b/stuff/projects/library/custom styles/clou.pli new file mode 100644 index 0000000..55b9ca0 Binary files /dev/null and b/stuff/projects/library/custom styles/clou.pli differ diff --git a/stuff/projects/library/custom styles/curl.pli b/stuff/projects/library/custom styles/curl.pli new file mode 100644 index 0000000..5155a41 Binary files /dev/null and b/stuff/projects/library/custom styles/curl.pli differ diff --git a/stuff/projects/library/custom styles/domino.0001.png b/stuff/projects/library/custom styles/domino.0001.png new file mode 100644 index 0000000..3708ee6 Binary files /dev/null and b/stuff/projects/library/custom styles/domino.0001.png differ diff --git a/stuff/projects/library/custom styles/domino.0002.png b/stuff/projects/library/custom styles/domino.0002.png new file mode 100644 index 0000000..6792a82 Binary files /dev/null and b/stuff/projects/library/custom styles/domino.0002.png differ diff --git a/stuff/projects/library/custom styles/domino.0003.png b/stuff/projects/library/custom styles/domino.0003.png new file mode 100644 index 0000000..17ee17d Binary files /dev/null and b/stuff/projects/library/custom styles/domino.0003.png differ diff --git a/stuff/projects/library/custom styles/domino.0004.png b/stuff/projects/library/custom styles/domino.0004.png new file mode 100644 index 0000000..264ed5e Binary files /dev/null and b/stuff/projects/library/custom styles/domino.0004.png differ diff --git a/stuff/projects/library/custom styles/domino.pli b/stuff/projects/library/custom styles/domino.pli new file mode 100644 index 0000000..2ee606a Binary files /dev/null and b/stuff/projects/library/custom styles/domino.pli differ diff --git a/stuff/projects/library/custom styles/dots.pli b/stuff/projects/library/custom styles/dots.pli new file mode 100644 index 0000000..0056ee1 Binary files /dev/null and b/stuff/projects/library/custom styles/dots.pli differ diff --git a/stuff/projects/library/custom styles/feat.pli b/stuff/projects/library/custom styles/feat.pli new file mode 100644 index 0000000..b07b3d8 Binary files /dev/null and b/stuff/projects/library/custom styles/feat.pli differ diff --git a/stuff/projects/library/custom styles/fish.pli b/stuff/projects/library/custom styles/fish.pli new file mode 100644 index 0000000..89b809f Binary files /dev/null and b/stuff/projects/library/custom styles/fish.pli differ diff --git a/stuff/projects/library/custom styles/flow.0001.png b/stuff/projects/library/custom styles/flow.0001.png new file mode 100644 index 0000000..c645299 Binary files /dev/null and b/stuff/projects/library/custom styles/flow.0001.png differ diff --git a/stuff/projects/library/custom styles/flow.0002.png b/stuff/projects/library/custom styles/flow.0002.png new file mode 100644 index 0000000..e6c5ab5 Binary files /dev/null and b/stuff/projects/library/custom styles/flow.0002.png differ diff --git a/stuff/projects/library/custom styles/flow.0003.png b/stuff/projects/library/custom styles/flow.0003.png new file mode 100644 index 0000000..567612e Binary files /dev/null and b/stuff/projects/library/custom styles/flow.0003.png differ diff --git a/stuff/projects/library/custom styles/flow.0004.png b/stuff/projects/library/custom styles/flow.0004.png new file mode 100644 index 0000000..6e8914a Binary files /dev/null and b/stuff/projects/library/custom styles/flow.0004.png differ diff --git a/stuff/projects/library/custom styles/flow.pli b/stuff/projects/library/custom styles/flow.pli new file mode 100644 index 0000000..1836a40 Binary files /dev/null and b/stuff/projects/library/custom styles/flow.pli differ diff --git a/stuff/projects/library/custom styles/flow3.0001.png b/stuff/projects/library/custom styles/flow3.0001.png new file mode 100644 index 0000000..2a007b0 Binary files /dev/null and b/stuff/projects/library/custom styles/flow3.0001.png differ diff --git a/stuff/projects/library/custom styles/flow3.0002.png b/stuff/projects/library/custom styles/flow3.0002.png new file mode 100644 index 0000000..e2925f0 Binary files /dev/null and b/stuff/projects/library/custom styles/flow3.0002.png differ diff --git a/stuff/projects/library/custom styles/flow3.0003.png b/stuff/projects/library/custom styles/flow3.0003.png new file mode 100644 index 0000000..278895e Binary files /dev/null and b/stuff/projects/library/custom styles/flow3.0003.png differ diff --git a/stuff/projects/library/custom styles/flow3.0004.png b/stuff/projects/library/custom styles/flow3.0004.png new file mode 100644 index 0000000..e9c5a9c Binary files /dev/null and b/stuff/projects/library/custom styles/flow3.0004.png differ diff --git a/stuff/projects/library/custom styles/flow3.pli b/stuff/projects/library/custom styles/flow3.pli new file mode 100644 index 0000000..9b22c82 Binary files /dev/null and b/stuff/projects/library/custom styles/flow3.pli differ diff --git a/stuff/projects/library/custom styles/flow4.0001.png b/stuff/projects/library/custom styles/flow4.0001.png new file mode 100644 index 0000000..39149bf Binary files /dev/null and b/stuff/projects/library/custom styles/flow4.0001.png differ diff --git a/stuff/projects/library/custom styles/flow4.0002.png b/stuff/projects/library/custom styles/flow4.0002.png new file mode 100644 index 0000000..979ca05 Binary files /dev/null and b/stuff/projects/library/custom styles/flow4.0002.png differ diff --git a/stuff/projects/library/custom styles/flow4.0003.png b/stuff/projects/library/custom styles/flow4.0003.png new file mode 100644 index 0000000..0770488 Binary files /dev/null and b/stuff/projects/library/custom styles/flow4.0003.png differ diff --git a/stuff/projects/library/custom styles/flow4.pli b/stuff/projects/library/custom styles/flow4.pli new file mode 100644 index 0000000..d0a0be5 Binary files /dev/null and b/stuff/projects/library/custom styles/flow4.pli differ diff --git a/stuff/projects/library/custom styles/flower.pli b/stuff/projects/library/custom styles/flower.pli new file mode 100644 index 0000000..6476eac Binary files /dev/null and b/stuff/projects/library/custom styles/flower.pli differ diff --git a/stuff/projects/library/custom styles/foot.pli b/stuff/projects/library/custom styles/foot.pli new file mode 100644 index 0000000..455e529 Binary files /dev/null and b/stuff/projects/library/custom styles/foot.pli differ diff --git a/stuff/projects/library/custom styles/grai.pli b/stuff/projects/library/custom styles/grai.pli new file mode 100644 index 0000000..cda24b8 Binary files /dev/null and b/stuff/projects/library/custom styles/grai.pli differ diff --git a/stuff/projects/library/custom styles/gras.pli b/stuff/projects/library/custom styles/gras.pli new file mode 100644 index 0000000..36d914c Binary files /dev/null and b/stuff/projects/library/custom styles/gras.pli differ diff --git a/stuff/projects/library/custom styles/gutt.pli b/stuff/projects/library/custom styles/gutt.pli new file mode 100644 index 0000000..fd1c3f5 Binary files /dev/null and b/stuff/projects/library/custom styles/gutt.pli differ diff --git a/stuff/projects/library/custom styles/half.0001.png b/stuff/projects/library/custom styles/half.0001.png new file mode 100644 index 0000000..d9e96ff Binary files /dev/null and b/stuff/projects/library/custom styles/half.0001.png differ diff --git a/stuff/projects/library/custom styles/half.0002.png b/stuff/projects/library/custom styles/half.0002.png new file mode 100644 index 0000000..d39a471 Binary files /dev/null and b/stuff/projects/library/custom styles/half.0002.png differ diff --git a/stuff/projects/library/custom styles/half.pli b/stuff/projects/library/custom styles/half.pli new file mode 100644 index 0000000..edbd405 Binary files /dev/null and b/stuff/projects/library/custom styles/half.pli differ diff --git a/stuff/projects/library/custom styles/hedg.0001.png b/stuff/projects/library/custom styles/hedg.0001.png new file mode 100644 index 0000000..ce69602 Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0001.png differ diff --git a/stuff/projects/library/custom styles/hedg.0002.png b/stuff/projects/library/custom styles/hedg.0002.png new file mode 100644 index 0000000..cc209e6 Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0002.png differ diff --git a/stuff/projects/library/custom styles/hedg.0003.png b/stuff/projects/library/custom styles/hedg.0003.png new file mode 100644 index 0000000..26a7bbf Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0003.png differ diff --git a/stuff/projects/library/custom styles/hedg.0004.png b/stuff/projects/library/custom styles/hedg.0004.png new file mode 100644 index 0000000..fc55139 Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0004.png differ diff --git a/stuff/projects/library/custom styles/hedg.0005.png b/stuff/projects/library/custom styles/hedg.0005.png new file mode 100644 index 0000000..6030f51 Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0005.png differ diff --git a/stuff/projects/library/custom styles/hedg.0006.png b/stuff/projects/library/custom styles/hedg.0006.png new file mode 100644 index 0000000..7fccc4a Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.0006.png differ diff --git a/stuff/projects/library/custom styles/hedg.pli b/stuff/projects/library/custom styles/hedg.pli new file mode 100644 index 0000000..fedab4c Binary files /dev/null and b/stuff/projects/library/custom styles/hedg.pli differ diff --git a/stuff/projects/library/custom styles/hook.pli b/stuff/projects/library/custom styles/hook.pli new file mode 100644 index 0000000..eac55e5 Binary files /dev/null and b/stuff/projects/library/custom styles/hook.pli differ diff --git a/stuff/projects/library/custom styles/ink.pli b/stuff/projects/library/custom styles/ink.pli new file mode 100644 index 0000000..3298f77 Binary files /dev/null and b/stuff/projects/library/custom styles/ink.pli differ diff --git a/stuff/projects/library/custom styles/leaf.pli b/stuff/projects/library/custom styles/leaf.pli new file mode 100644 index 0000000..9cff437 Binary files /dev/null and b/stuff/projects/library/custom styles/leaf.pli differ diff --git a/stuff/projects/library/custom styles/myra.pli b/stuff/projects/library/custom styles/myra.pli new file mode 100644 index 0000000..dff5822 Binary files /dev/null and b/stuff/projects/library/custom styles/myra.pli differ diff --git a/stuff/projects/library/custom styles/pansee.pli b/stuff/projects/library/custom styles/pansee.pli new file mode 100644 index 0000000..4ffec9a Binary files /dev/null and b/stuff/projects/library/custom styles/pansee.pli differ diff --git a/stuff/projects/library/custom styles/pare.0001.png b/stuff/projects/library/custom styles/pare.0001.png new file mode 100644 index 0000000..ebc6ac3 Binary files /dev/null and b/stuff/projects/library/custom styles/pare.0001.png differ diff --git a/stuff/projects/library/custom styles/pare.0002.png b/stuff/projects/library/custom styles/pare.0002.png new file mode 100644 index 0000000..5e440e2 Binary files /dev/null and b/stuff/projects/library/custom styles/pare.0002.png differ diff --git a/stuff/projects/library/custom styles/pare.pli b/stuff/projects/library/custom styles/pare.pli new file mode 100644 index 0000000..b2d0ab4 Binary files /dev/null and b/stuff/projects/library/custom styles/pare.pli differ diff --git a/stuff/projects/library/custom styles/pare2.0001.png b/stuff/projects/library/custom styles/pare2.0001.png new file mode 100644 index 0000000..d0f0914 Binary files /dev/null and b/stuff/projects/library/custom styles/pare2.0001.png differ diff --git a/stuff/projects/library/custom styles/pare2.0002.png b/stuff/projects/library/custom styles/pare2.0002.png new file mode 100644 index 0000000..190c24b Binary files /dev/null and b/stuff/projects/library/custom styles/pare2.0002.png differ diff --git a/stuff/projects/library/custom styles/pare2.pli b/stuff/projects/library/custom styles/pare2.pli new file mode 100644 index 0000000..bd75c09 Binary files /dev/null and b/stuff/projects/library/custom styles/pare2.pli differ diff --git a/stuff/projects/library/custom styles/plum.pli b/stuff/projects/library/custom styles/plum.pli new file mode 100644 index 0000000..28e54be Binary files /dev/null and b/stuff/projects/library/custom styles/plum.pli differ diff --git a/stuff/projects/library/custom styles/rain.pli b/stuff/projects/library/custom styles/rain.pli new file mode 100644 index 0000000..295a05b Binary files /dev/null and b/stuff/projects/library/custom styles/rain.pli differ diff --git a/stuff/projects/library/custom styles/rice.0001.png b/stuff/projects/library/custom styles/rice.0001.png new file mode 100644 index 0000000..267a4e1 Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0001.png differ diff --git a/stuff/projects/library/custom styles/rice.0002.png b/stuff/projects/library/custom styles/rice.0002.png new file mode 100644 index 0000000..b19727d Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0002.png differ diff --git a/stuff/projects/library/custom styles/rice.0003.png b/stuff/projects/library/custom styles/rice.0003.png new file mode 100644 index 0000000..bb78d5d Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0003.png differ diff --git a/stuff/projects/library/custom styles/rice.0004.png b/stuff/projects/library/custom styles/rice.0004.png new file mode 100644 index 0000000..543bc4c Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0004.png differ diff --git a/stuff/projects/library/custom styles/rice.0005.png b/stuff/projects/library/custom styles/rice.0005.png new file mode 100644 index 0000000..e6a431e Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0005.png differ diff --git a/stuff/projects/library/custom styles/rice.0006.png b/stuff/projects/library/custom styles/rice.0006.png new file mode 100644 index 0000000..69cc090 Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0006.png differ diff --git a/stuff/projects/library/custom styles/rice.0007.png b/stuff/projects/library/custom styles/rice.0007.png new file mode 100644 index 0000000..1988a3a Binary files /dev/null and b/stuff/projects/library/custom styles/rice.0007.png differ diff --git a/stuff/projects/library/custom styles/rice.pli b/stuff/projects/library/custom styles/rice.pli new file mode 100644 index 0000000..2cc60f4 Binary files /dev/null and b/stuff/projects/library/custom styles/rice.pli differ diff --git a/stuff/projects/library/custom styles/sang.pli b/stuff/projects/library/custom styles/sang.pli new file mode 100644 index 0000000..47910ec Binary files /dev/null and b/stuff/projects/library/custom styles/sang.pli differ diff --git a/stuff/projects/library/custom styles/scal.pli b/stuff/projects/library/custom styles/scal.pli new file mode 100644 index 0000000..8de316e Binary files /dev/null and b/stuff/projects/library/custom styles/scal.pli differ diff --git a/stuff/projects/library/custom styles/scho.pli b/stuff/projects/library/custom styles/scho.pli new file mode 100644 index 0000000..be4ec7a Binary files /dev/null and b/stuff/projects/library/custom styles/scho.pli differ diff --git a/stuff/projects/library/custom styles/sign.pli b/stuff/projects/library/custom styles/sign.pli new file mode 100644 index 0000000..4878619 Binary files /dev/null and b/stuff/projects/library/custom styles/sign.pli differ diff --git a/stuff/projects/library/custom styles/stai.0001.png b/stuff/projects/library/custom styles/stai.0001.png new file mode 100644 index 0000000..844584d Binary files /dev/null and b/stuff/projects/library/custom styles/stai.0001.png differ diff --git a/stuff/projects/library/custom styles/stai.0002.png b/stuff/projects/library/custom styles/stai.0002.png new file mode 100644 index 0000000..82f14f9 Binary files /dev/null and b/stuff/projects/library/custom styles/stai.0002.png differ diff --git a/stuff/projects/library/custom styles/stai.0003.png b/stuff/projects/library/custom styles/stai.0003.png new file mode 100644 index 0000000..a79130e Binary files /dev/null and b/stuff/projects/library/custom styles/stai.0003.png differ diff --git a/stuff/projects/library/custom styles/stai.0004.png b/stuff/projects/library/custom styles/stai.0004.png new file mode 100644 index 0000000..6a25cb8 Binary files /dev/null and b/stuff/projects/library/custom styles/stai.0004.png differ diff --git a/stuff/projects/library/custom styles/stai.pli b/stuff/projects/library/custom styles/stai.pli new file mode 100644 index 0000000..7971565 Binary files /dev/null and b/stuff/projects/library/custom styles/stai.pli differ diff --git a/stuff/projects/library/custom styles/star.0001.png b/stuff/projects/library/custom styles/star.0001.png new file mode 100644 index 0000000..a2b9ef6 Binary files /dev/null and b/stuff/projects/library/custom styles/star.0001.png differ diff --git a/stuff/projects/library/custom styles/star.0002.png b/stuff/projects/library/custom styles/star.0002.png new file mode 100644 index 0000000..b7707ee Binary files /dev/null and b/stuff/projects/library/custom styles/star.0002.png differ diff --git a/stuff/projects/library/custom styles/star.0003.png b/stuff/projects/library/custom styles/star.0003.png new file mode 100644 index 0000000..a575c1b Binary files /dev/null and b/stuff/projects/library/custom styles/star.0003.png differ diff --git a/stuff/projects/library/custom styles/star.0004.png b/stuff/projects/library/custom styles/star.0004.png new file mode 100644 index 0000000..ae3d4e6 Binary files /dev/null and b/stuff/projects/library/custom styles/star.0004.png differ diff --git a/stuff/projects/library/custom styles/star.0005.png b/stuff/projects/library/custom styles/star.0005.png new file mode 100644 index 0000000..dcb9199 Binary files /dev/null and b/stuff/projects/library/custom styles/star.0005.png differ diff --git a/stuff/projects/library/custom styles/star.0006.png b/stuff/projects/library/custom styles/star.0006.png new file mode 100644 index 0000000..070234f Binary files /dev/null and b/stuff/projects/library/custom styles/star.0006.png differ diff --git a/stuff/projects/library/custom styles/star.0007.png b/stuff/projects/library/custom styles/star.0007.png new file mode 100644 index 0000000..d4b2f3e Binary files /dev/null and b/stuff/projects/library/custom styles/star.0007.png differ diff --git a/stuff/projects/library/custom styles/star.0008.png b/stuff/projects/library/custom styles/star.0008.png new file mode 100644 index 0000000..9467f8f Binary files /dev/null and b/stuff/projects/library/custom styles/star.0008.png differ diff --git a/stuff/projects/library/custom styles/star.0009.png b/stuff/projects/library/custom styles/star.0009.png new file mode 100644 index 0000000..1df1ac0 Binary files /dev/null and b/stuff/projects/library/custom styles/star.0009.png differ diff --git a/stuff/projects/library/custom styles/star.pli b/stuff/projects/library/custom styles/star.pli new file mode 100644 index 0000000..e799bec Binary files /dev/null and b/stuff/projects/library/custom styles/star.pli differ diff --git a/stuff/projects/library/custom styles/thor.pli b/stuff/projects/library/custom styles/thor.pli new file mode 100644 index 0000000..a4c856a Binary files /dev/null and b/stuff/projects/library/custom styles/thor.pli differ diff --git a/stuff/projects/library/custom styles/thor2.pli b/stuff/projects/library/custom styles/thor2.pli new file mode 100644 index 0000000..2504e69 Binary files /dev/null and b/stuff/projects/library/custom styles/thor2.pli differ diff --git a/stuff/projects/library/custom styles/wave.pli b/stuff/projects/library/custom styles/wave.pli new file mode 100644 index 0000000..7b6de65 Binary files /dev/null and b/stuff/projects/library/custom styles/wave.pli differ diff --git a/stuff/projects/library/custom styles/wave2.pli b/stuff/projects/library/custom styles/wave2.pli new file mode 100644 index 0000000..e65ca28 Binary files /dev/null and b/stuff/projects/library/custom styles/wave2.pli differ diff --git a/stuff/projects/library/particles/Thumbs.db b/stuff/projects/library/particles/Thumbs.db new file mode 100644 index 0000000..e6106fd Binary files /dev/null and b/stuff/projects/library/particles/Thumbs.db differ diff --git a/stuff/projects/library/particles/beehive.0001.tif b/stuff/projects/library/particles/beehive.0001.tif new file mode 100644 index 0000000..636d2dc Binary files /dev/null and b/stuff/projects/library/particles/beehive.0001.tif differ diff --git a/stuff/projects/library/particles/bird.0001.tif b/stuff/projects/library/particles/bird.0001.tif new file mode 100644 index 0000000..cc67c98 Binary files /dev/null and b/stuff/projects/library/particles/bird.0001.tif differ diff --git a/stuff/projects/library/particles/bird.0002.tif b/stuff/projects/library/particles/bird.0002.tif new file mode 100644 index 0000000..0eee75b Binary files /dev/null and b/stuff/projects/library/particles/bird.0002.tif differ diff --git a/stuff/projects/library/particles/bird.0003.tif b/stuff/projects/library/particles/bird.0003.tif new file mode 100644 index 0000000..dcc4cb5 Binary files /dev/null and b/stuff/projects/library/particles/bird.0003.tif differ diff --git a/stuff/projects/library/particles/bird.0004.tif b/stuff/projects/library/particles/bird.0004.tif new file mode 100644 index 0000000..7616644 Binary files /dev/null and b/stuff/projects/library/particles/bird.0004.tif differ diff --git a/stuff/projects/library/particles/bird.0005.tif b/stuff/projects/library/particles/bird.0005.tif new file mode 100644 index 0000000..11ffa3f Binary files /dev/null and b/stuff/projects/library/particles/bird.0005.tif differ diff --git a/stuff/projects/library/particles/bird.0006.tif b/stuff/projects/library/particles/bird.0006.tif new file mode 100644 index 0000000..976832a Binary files /dev/null and b/stuff/projects/library/particles/bird.0006.tif differ diff --git a/stuff/projects/library/particles/bird.0007.tif b/stuff/projects/library/particles/bird.0007.tif new file mode 100644 index 0000000..b6d1c9d Binary files /dev/null and b/stuff/projects/library/particles/bird.0007.tif differ diff --git a/stuff/projects/library/particles/bird.0008.tif b/stuff/projects/library/particles/bird.0008.tif new file mode 100644 index 0000000..17bb0a5 Binary files /dev/null and b/stuff/projects/library/particles/bird.0008.tif differ diff --git a/stuff/projects/library/particles/bird.0009.tif b/stuff/projects/library/particles/bird.0009.tif new file mode 100644 index 0000000..79a784f Binary files /dev/null and b/stuff/projects/library/particles/bird.0009.tif differ diff --git a/stuff/projects/library/particles/bird.0010.tif b/stuff/projects/library/particles/bird.0010.tif new file mode 100644 index 0000000..7f4be65 Binary files /dev/null and b/stuff/projects/library/particles/bird.0010.tif differ diff --git a/stuff/projects/library/particles/bird.0011.tif b/stuff/projects/library/particles/bird.0011.tif new file mode 100644 index 0000000..e809c1b Binary files /dev/null and b/stuff/projects/library/particles/bird.0011.tif differ diff --git a/stuff/projects/library/particles/bird.0012.tif b/stuff/projects/library/particles/bird.0012.tif new file mode 100644 index 0000000..fd6dc99 Binary files /dev/null and b/stuff/projects/library/particles/bird.0012.tif differ diff --git a/stuff/projects/library/particles/bird2.0001.tif b/stuff/projects/library/particles/bird2.0001.tif new file mode 100644 index 0000000..90d800f Binary files /dev/null and b/stuff/projects/library/particles/bird2.0001.tif differ diff --git a/stuff/projects/library/particles/bird2.0002.tif b/stuff/projects/library/particles/bird2.0002.tif new file mode 100644 index 0000000..97308f0 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0002.tif differ diff --git a/stuff/projects/library/particles/bird2.0003.tif b/stuff/projects/library/particles/bird2.0003.tif new file mode 100644 index 0000000..746c9fd Binary files /dev/null and b/stuff/projects/library/particles/bird2.0003.tif differ diff --git a/stuff/projects/library/particles/bird2.0004.tif b/stuff/projects/library/particles/bird2.0004.tif new file mode 100644 index 0000000..990b25d Binary files /dev/null and b/stuff/projects/library/particles/bird2.0004.tif differ diff --git a/stuff/projects/library/particles/bird2.0005.tif b/stuff/projects/library/particles/bird2.0005.tif new file mode 100644 index 0000000..2b38838 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0005.tif differ diff --git a/stuff/projects/library/particles/bird2.0006.tif b/stuff/projects/library/particles/bird2.0006.tif new file mode 100644 index 0000000..9e75ecf Binary files /dev/null and b/stuff/projects/library/particles/bird2.0006.tif differ diff --git a/stuff/projects/library/particles/bird2.0007.tif b/stuff/projects/library/particles/bird2.0007.tif new file mode 100644 index 0000000..c93b09e Binary files /dev/null and b/stuff/projects/library/particles/bird2.0007.tif differ diff --git a/stuff/projects/library/particles/bird2.0008.tif b/stuff/projects/library/particles/bird2.0008.tif new file mode 100644 index 0000000..3057d82 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0008.tif differ diff --git a/stuff/projects/library/particles/bird2.0009.tif b/stuff/projects/library/particles/bird2.0009.tif new file mode 100644 index 0000000..e56743f Binary files /dev/null and b/stuff/projects/library/particles/bird2.0009.tif differ diff --git a/stuff/projects/library/particles/bird2.0010.tif b/stuff/projects/library/particles/bird2.0010.tif new file mode 100644 index 0000000..ef8db1e Binary files /dev/null and b/stuff/projects/library/particles/bird2.0010.tif differ diff --git a/stuff/projects/library/particles/bird2.0011.tif b/stuff/projects/library/particles/bird2.0011.tif new file mode 100644 index 0000000..ffdf166 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0011.tif differ diff --git a/stuff/projects/library/particles/bird2.0012.tif b/stuff/projects/library/particles/bird2.0012.tif new file mode 100644 index 0000000..42ebe08 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0012.tif differ diff --git a/stuff/projects/library/particles/bird2.0013.tif b/stuff/projects/library/particles/bird2.0013.tif new file mode 100644 index 0000000..5af2169 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0013.tif differ diff --git a/stuff/projects/library/particles/bird2.0014.tif b/stuff/projects/library/particles/bird2.0014.tif new file mode 100644 index 0000000..ef8db1e Binary files /dev/null and b/stuff/projects/library/particles/bird2.0014.tif differ diff --git a/stuff/projects/library/particles/bird2.0015.tif b/stuff/projects/library/particles/bird2.0015.tif new file mode 100644 index 0000000..e56743f Binary files /dev/null and b/stuff/projects/library/particles/bird2.0015.tif differ diff --git a/stuff/projects/library/particles/bird2.0016.tif b/stuff/projects/library/particles/bird2.0016.tif new file mode 100644 index 0000000..3057d82 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0016.tif differ diff --git a/stuff/projects/library/particles/bird2.0017.tif b/stuff/projects/library/particles/bird2.0017.tif new file mode 100644 index 0000000..c93b09e Binary files /dev/null and b/stuff/projects/library/particles/bird2.0017.tif differ diff --git a/stuff/projects/library/particles/bird2.0018.tif b/stuff/projects/library/particles/bird2.0018.tif new file mode 100644 index 0000000..9e75ecf Binary files /dev/null and b/stuff/projects/library/particles/bird2.0018.tif differ diff --git a/stuff/projects/library/particles/bird2.0019.tif b/stuff/projects/library/particles/bird2.0019.tif new file mode 100644 index 0000000..2b38838 Binary files /dev/null and b/stuff/projects/library/particles/bird2.0019.tif differ diff --git a/stuff/projects/library/particles/bird2.0020.tif b/stuff/projects/library/particles/bird2.0020.tif new file mode 100644 index 0000000..990b25d Binary files /dev/null and b/stuff/projects/library/particles/bird2.0020.tif differ diff --git a/stuff/projects/library/particles/bird2.0021.tif b/stuff/projects/library/particles/bird2.0021.tif new file mode 100644 index 0000000..746c9fd Binary files /dev/null and b/stuff/projects/library/particles/bird2.0021.tif differ diff --git a/stuff/projects/library/particles/bird2.0022.tif b/stuff/projects/library/particles/bird2.0022.tif new file mode 100644 index 0000000..b606dac Binary files /dev/null and b/stuff/projects/library/particles/bird2.0022.tif differ diff --git a/stuff/projects/library/particles/brushstroke.0001.tif b/stuff/projects/library/particles/brushstroke.0001.tif new file mode 100644 index 0000000..2b891e7 Binary files /dev/null and b/stuff/projects/library/particles/brushstroke.0001.tif differ diff --git a/stuff/projects/library/particles/bubble.0001.tif b/stuff/projects/library/particles/bubble.0001.tif new file mode 100644 index 0000000..18a4aed Binary files /dev/null and b/stuff/projects/library/particles/bubble.0001.tif differ diff --git a/stuff/projects/library/particles/coalstroke.0001.tif b/stuff/projects/library/particles/coalstroke.0001.tif new file mode 100644 index 0000000..42b46af Binary files /dev/null and b/stuff/projects/library/particles/coalstroke.0001.tif differ diff --git a/stuff/projects/library/particles/crater.0001.tif b/stuff/projects/library/particles/crater.0001.tif new file mode 100644 index 0000000..1a68b34 Binary files /dev/null and b/stuff/projects/library/particles/crater.0001.tif differ diff --git a/stuff/projects/library/particles/crayon.0001.tif b/stuff/projects/library/particles/crayon.0001.tif new file mode 100644 index 0000000..b60f6e3 Binary files /dev/null and b/stuff/projects/library/particles/crayon.0001.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0070.tif b/stuff/projects/library/particles/dragonfly.0070.tif new file mode 100644 index 0000000..9f68f07 Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0070.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0071.tif b/stuff/projects/library/particles/dragonfly.0071.tif new file mode 100644 index 0000000..e48405b Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0071.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0072.tif b/stuff/projects/library/particles/dragonfly.0072.tif new file mode 100644 index 0000000..0aedd3d Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0072.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0073.tif b/stuff/projects/library/particles/dragonfly.0073.tif new file mode 100644 index 0000000..21fec01 Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0073.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0074.tif b/stuff/projects/library/particles/dragonfly.0074.tif new file mode 100644 index 0000000..6c1940f Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0074.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0075.tif b/stuff/projects/library/particles/dragonfly.0075.tif new file mode 100644 index 0000000..4820bae Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0075.tif differ diff --git a/stuff/projects/library/particles/dragonfly.0076.tif b/stuff/projects/library/particles/dragonfly.0076.tif new file mode 100644 index 0000000..9d5400d Binary files /dev/null and b/stuff/projects/library/particles/dragonfly.0076.tif differ diff --git a/stuff/projects/library/particles/drip.0001.tif b/stuff/projects/library/particles/drip.0001.tif new file mode 100644 index 0000000..89352f8 Binary files /dev/null and b/stuff/projects/library/particles/drip.0001.tif differ diff --git a/stuff/projects/library/particles/drop.0001.rgb b/stuff/projects/library/particles/drop.0001.rgb new file mode 100644 index 0000000..8456d09 Binary files /dev/null and b/stuff/projects/library/particles/drop.0001.rgb differ diff --git a/stuff/projects/library/particles/drop2.0001.tif b/stuff/projects/library/particles/drop2.0001.tif new file mode 100644 index 0000000..33c236f Binary files /dev/null and b/stuff/projects/library/particles/drop2.0001.tif differ diff --git a/stuff/projects/library/particles/fire.0001.tif b/stuff/projects/library/particles/fire.0001.tif new file mode 100644 index 0000000..e7c1047 Binary files /dev/null and b/stuff/projects/library/particles/fire.0001.tif differ diff --git a/stuff/projects/library/particles/fossil.0001.tif b/stuff/projects/library/particles/fossil.0001.tif new file mode 100644 index 0000000..80fc5c7 Binary files /dev/null and b/stuff/projects/library/particles/fossil.0001.tif differ diff --git a/stuff/projects/library/particles/fountainpen.0001.tif b/stuff/projects/library/particles/fountainpen.0001.tif new file mode 100644 index 0000000..33288d0 Binary files /dev/null and b/stuff/projects/library/particles/fountainpen.0001.tif differ diff --git a/stuff/projects/library/particles/grass.0001.rgb b/stuff/projects/library/particles/grass.0001.rgb new file mode 100644 index 0000000..ce15686 Binary files /dev/null and b/stuff/projects/library/particles/grass.0001.rgb differ diff --git a/stuff/projects/library/particles/grass.0002.rgb b/stuff/projects/library/particles/grass.0002.rgb new file mode 100644 index 0000000..b00af5a Binary files /dev/null and b/stuff/projects/library/particles/grass.0002.rgb differ diff --git a/stuff/projects/library/particles/grass.0003.rgb b/stuff/projects/library/particles/grass.0003.rgb new file mode 100644 index 0000000..f932cfc Binary files /dev/null and b/stuff/projects/library/particles/grass.0003.rgb differ diff --git a/stuff/projects/library/particles/grass.0004.rgb b/stuff/projects/library/particles/grass.0004.rgb new file mode 100644 index 0000000..c2c4af5 Binary files /dev/null and b/stuff/projects/library/particles/grass.0004.rgb differ diff --git a/stuff/projects/library/particles/grass.0005.rgb b/stuff/projects/library/particles/grass.0005.rgb new file mode 100644 index 0000000..d9d2529 Binary files /dev/null and b/stuff/projects/library/particles/grass.0005.rgb differ diff --git a/stuff/projects/library/particles/grass.0006.rgb b/stuff/projects/library/particles/grass.0006.rgb new file mode 100644 index 0000000..fe78991 Binary files /dev/null and b/stuff/projects/library/particles/grass.0006.rgb differ diff --git a/stuff/projects/library/particles/grass.0007.rgb b/stuff/projects/library/particles/grass.0007.rgb new file mode 100644 index 0000000..297f127 Binary files /dev/null and b/stuff/projects/library/particles/grass.0007.rgb differ diff --git a/stuff/projects/library/particles/grass.0008.rgb b/stuff/projects/library/particles/grass.0008.rgb new file mode 100644 index 0000000..bdb7a87 Binary files /dev/null and b/stuff/projects/library/particles/grass.0008.rgb differ diff --git a/stuff/projects/library/particles/grass.0009.rgb b/stuff/projects/library/particles/grass.0009.rgb new file mode 100644 index 0000000..90b9688 Binary files /dev/null and b/stuff/projects/library/particles/grass.0009.rgb differ diff --git a/stuff/projects/library/particles/grass.0010.rgb b/stuff/projects/library/particles/grass.0010.rgb new file mode 100644 index 0000000..152a080 Binary files /dev/null and b/stuff/projects/library/particles/grass.0010.rgb differ diff --git a/stuff/projects/library/particles/grass.0011.rgb b/stuff/projects/library/particles/grass.0011.rgb new file mode 100644 index 0000000..d106569 Binary files /dev/null and b/stuff/projects/library/particles/grass.0011.rgb differ diff --git a/stuff/projects/library/particles/grass.0012.rgb b/stuff/projects/library/particles/grass.0012.rgb new file mode 100644 index 0000000..5e2c96c Binary files /dev/null and b/stuff/projects/library/particles/grass.0012.rgb differ diff --git a/stuff/projects/library/particles/grass.0013.rgb b/stuff/projects/library/particles/grass.0013.rgb new file mode 100644 index 0000000..b5cc3b8 Binary files /dev/null and b/stuff/projects/library/particles/grass.0013.rgb differ diff --git a/stuff/projects/library/particles/grass.0014.rgb b/stuff/projects/library/particles/grass.0014.rgb new file mode 100644 index 0000000..f49f5f2 Binary files /dev/null and b/stuff/projects/library/particles/grass.0014.rgb differ diff --git a/stuff/projects/library/particles/grass.0015.rgb b/stuff/projects/library/particles/grass.0015.rgb new file mode 100644 index 0000000..0235000 Binary files /dev/null and b/stuff/projects/library/particles/grass.0015.rgb differ diff --git a/stuff/projects/library/particles/grass.0016.rgb b/stuff/projects/library/particles/grass.0016.rgb new file mode 100644 index 0000000..14aa457 Binary files /dev/null and b/stuff/projects/library/particles/grass.0016.rgb differ diff --git a/stuff/projects/library/particles/leaf.0001.rgb b/stuff/projects/library/particles/leaf.0001.rgb new file mode 100644 index 0000000..4c54a9e Binary files /dev/null and b/stuff/projects/library/particles/leaf.0001.rgb differ diff --git a/stuff/projects/library/particles/leaf.0002.rgb b/stuff/projects/library/particles/leaf.0002.rgb new file mode 100644 index 0000000..8e3cfee Binary files /dev/null and b/stuff/projects/library/particles/leaf.0002.rgb differ diff --git a/stuff/projects/library/particles/leaf.0003.rgb b/stuff/projects/library/particles/leaf.0003.rgb new file mode 100644 index 0000000..b5283e3 Binary files /dev/null and b/stuff/projects/library/particles/leaf.0003.rgb differ diff --git a/stuff/projects/library/particles/leaf.0004.rgb b/stuff/projects/library/particles/leaf.0004.rgb new file mode 100644 index 0000000..6ea7670 Binary files /dev/null and b/stuff/projects/library/particles/leaf.0004.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0001.rgb b/stuff/projects/library/particles/leaf2.0001.rgb new file mode 100644 index 0000000..dacb142 Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0001.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0002.rgb b/stuff/projects/library/particles/leaf2.0002.rgb new file mode 100644 index 0000000..73270db Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0002.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0003.rgb b/stuff/projects/library/particles/leaf2.0003.rgb new file mode 100644 index 0000000..c0a55fc Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0003.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0004.rgb b/stuff/projects/library/particles/leaf2.0004.rgb new file mode 100644 index 0000000..654e8e4 Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0004.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0005.rgb b/stuff/projects/library/particles/leaf2.0005.rgb new file mode 100644 index 0000000..0f0bde8 Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0005.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0006.rgb b/stuff/projects/library/particles/leaf2.0006.rgb new file mode 100644 index 0000000..271f820 Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0006.rgb differ diff --git a/stuff/projects/library/particles/leaf2.0007.rgb b/stuff/projects/library/particles/leaf2.0007.rgb new file mode 100644 index 0000000..1a430c6 Binary files /dev/null and b/stuff/projects/library/particles/leaf2.0007.rgb differ diff --git a/stuff/projects/library/particles/opripple.0001.tif b/stuff/projects/library/particles/opripple.0001.tif new file mode 100644 index 0000000..5443929 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0001.tif differ diff --git a/stuff/projects/library/particles/opripple.0002.tif b/stuff/projects/library/particles/opripple.0002.tif new file mode 100644 index 0000000..8c358ca Binary files /dev/null and b/stuff/projects/library/particles/opripple.0002.tif differ diff --git a/stuff/projects/library/particles/opripple.0003.tif b/stuff/projects/library/particles/opripple.0003.tif new file mode 100644 index 0000000..74f3833 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0003.tif differ diff --git a/stuff/projects/library/particles/opripple.0004.tif b/stuff/projects/library/particles/opripple.0004.tif new file mode 100644 index 0000000..d582044 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0004.tif differ diff --git a/stuff/projects/library/particles/opripple.0005.tif b/stuff/projects/library/particles/opripple.0005.tif new file mode 100644 index 0000000..0cb209d Binary files /dev/null and b/stuff/projects/library/particles/opripple.0005.tif differ diff --git a/stuff/projects/library/particles/opripple.0006.tif b/stuff/projects/library/particles/opripple.0006.tif new file mode 100644 index 0000000..9cd5c85 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0006.tif differ diff --git a/stuff/projects/library/particles/opripple.0007.tif b/stuff/projects/library/particles/opripple.0007.tif new file mode 100644 index 0000000..aa4424a Binary files /dev/null and b/stuff/projects/library/particles/opripple.0007.tif differ diff --git a/stuff/projects/library/particles/opripple.0008.tif b/stuff/projects/library/particles/opripple.0008.tif new file mode 100644 index 0000000..36a56a6 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0008.tif differ diff --git a/stuff/projects/library/particles/opripple.0009.tif b/stuff/projects/library/particles/opripple.0009.tif new file mode 100644 index 0000000..68ae515 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0009.tif differ diff --git a/stuff/projects/library/particles/opripple.0010.tif b/stuff/projects/library/particles/opripple.0010.tif new file mode 100644 index 0000000..8cff25f Binary files /dev/null and b/stuff/projects/library/particles/opripple.0010.tif differ diff --git a/stuff/projects/library/particles/opripple.0011.tif b/stuff/projects/library/particles/opripple.0011.tif new file mode 100644 index 0000000..c04abfe Binary files /dev/null and b/stuff/projects/library/particles/opripple.0011.tif differ diff --git a/stuff/projects/library/particles/opripple.0012.tif b/stuff/projects/library/particles/opripple.0012.tif new file mode 100644 index 0000000..63e4a88 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0012.tif differ diff --git a/stuff/projects/library/particles/opripple.0013.tif b/stuff/projects/library/particles/opripple.0013.tif new file mode 100644 index 0000000..176b85f Binary files /dev/null and b/stuff/projects/library/particles/opripple.0013.tif differ diff --git a/stuff/projects/library/particles/opripple.0014.tif b/stuff/projects/library/particles/opripple.0014.tif new file mode 100644 index 0000000..a56af93 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0014.tif differ diff --git a/stuff/projects/library/particles/opripple.0015.tif b/stuff/projects/library/particles/opripple.0015.tif new file mode 100644 index 0000000..3a0ab3a Binary files /dev/null and b/stuff/projects/library/particles/opripple.0015.tif differ diff --git a/stuff/projects/library/particles/opripple.0016.tif b/stuff/projects/library/particles/opripple.0016.tif new file mode 100644 index 0000000..5dcd584 Binary files /dev/null and b/stuff/projects/library/particles/opripple.0016.tif differ diff --git a/stuff/projects/library/particles/point.0001.rgb b/stuff/projects/library/particles/point.0001.rgb new file mode 100644 index 0000000..3c3858a Binary files /dev/null and b/stuff/projects/library/particles/point.0001.rgb differ diff --git a/stuff/projects/library/particles/raindrop.0001.tif b/stuff/projects/library/particles/raindrop.0001.tif new file mode 100644 index 0000000..a5d054d Binary files /dev/null and b/stuff/projects/library/particles/raindrop.0001.tif differ diff --git a/stuff/projects/library/particles/scrawl.0001.tif b/stuff/projects/library/particles/scrawl.0001.tif new file mode 100644 index 0000000..be7e7fc Binary files /dev/null and b/stuff/projects/library/particles/scrawl.0001.tif differ diff --git a/stuff/projects/library/particles/smoke.0001.tif b/stuff/projects/library/particles/smoke.0001.tif new file mode 100644 index 0000000..ca1f3aa Binary files /dev/null and b/stuff/projects/library/particles/smoke.0001.tif differ diff --git a/stuff/projects/library/particles/smoke.0002.tif b/stuff/projects/library/particles/smoke.0002.tif new file mode 100644 index 0000000..aa5407e Binary files /dev/null and b/stuff/projects/library/particles/smoke.0002.tif differ diff --git a/stuff/projects/library/particles/smoke.0003.tif b/stuff/projects/library/particles/smoke.0003.tif new file mode 100644 index 0000000..21b4916 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0003.tif differ diff --git a/stuff/projects/library/particles/smoke.0004.tif b/stuff/projects/library/particles/smoke.0004.tif new file mode 100644 index 0000000..1d715f7 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0004.tif differ diff --git a/stuff/projects/library/particles/smoke.0005.tif b/stuff/projects/library/particles/smoke.0005.tif new file mode 100644 index 0000000..4e70f16 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0005.tif differ diff --git a/stuff/projects/library/particles/smoke.0006.tif b/stuff/projects/library/particles/smoke.0006.tif new file mode 100644 index 0000000..9a84264 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0006.tif differ diff --git a/stuff/projects/library/particles/smoke.0007.tif b/stuff/projects/library/particles/smoke.0007.tif new file mode 100644 index 0000000..45372d7 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0007.tif differ diff --git a/stuff/projects/library/particles/smoke.0008.tif b/stuff/projects/library/particles/smoke.0008.tif new file mode 100644 index 0000000..7213a0a Binary files /dev/null and b/stuff/projects/library/particles/smoke.0008.tif differ diff --git a/stuff/projects/library/particles/smoke.0009.tif b/stuff/projects/library/particles/smoke.0009.tif new file mode 100644 index 0000000..97cf4a6 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0009.tif differ diff --git a/stuff/projects/library/particles/smoke.0010.tif b/stuff/projects/library/particles/smoke.0010.tif new file mode 100644 index 0000000..e4bfc65 Binary files /dev/null and b/stuff/projects/library/particles/smoke.0010.tif differ diff --git a/stuff/projects/library/particles/snowflake.0001.tif b/stuff/projects/library/particles/snowflake.0001.tif new file mode 100644 index 0000000..e751a10 Binary files /dev/null and b/stuff/projects/library/particles/snowflake.0001.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0001.tif b/stuff/projects/library/particles/snowflakerot.0001.tif new file mode 100644 index 0000000..39f0bc9 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0001.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0002.tif b/stuff/projects/library/particles/snowflakerot.0002.tif new file mode 100644 index 0000000..c0173d8 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0002.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0003.tif b/stuff/projects/library/particles/snowflakerot.0003.tif new file mode 100644 index 0000000..1b48d09 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0003.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0004.tif b/stuff/projects/library/particles/snowflakerot.0004.tif new file mode 100644 index 0000000..b63e0f1 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0004.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0005.tif b/stuff/projects/library/particles/snowflakerot.0005.tif new file mode 100644 index 0000000..b3b8c36 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0005.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0006.tif b/stuff/projects/library/particles/snowflakerot.0006.tif new file mode 100644 index 0000000..424bbca Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0006.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0007.tif b/stuff/projects/library/particles/snowflakerot.0007.tif new file mode 100644 index 0000000..58a134a Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0007.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0008.tif b/stuff/projects/library/particles/snowflakerot.0008.tif new file mode 100644 index 0000000..0a344b8 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0008.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0009.tif b/stuff/projects/library/particles/snowflakerot.0009.tif new file mode 100644 index 0000000..b5d4e85 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0009.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0010.tif b/stuff/projects/library/particles/snowflakerot.0010.tif new file mode 100644 index 0000000..a6913fb Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0010.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0011.tif b/stuff/projects/library/particles/snowflakerot.0011.tif new file mode 100644 index 0000000..93ecf96 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0011.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0012.tif b/stuff/projects/library/particles/snowflakerot.0012.tif new file mode 100644 index 0000000..1cca24a Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0012.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0013.tif b/stuff/projects/library/particles/snowflakerot.0013.tif new file mode 100644 index 0000000..6db9752 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0013.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0014.tif b/stuff/projects/library/particles/snowflakerot.0014.tif new file mode 100644 index 0000000..3804dfc Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0014.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0015.tif b/stuff/projects/library/particles/snowflakerot.0015.tif new file mode 100644 index 0000000..ae81a7c Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0015.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0016.tif b/stuff/projects/library/particles/snowflakerot.0016.tif new file mode 100644 index 0000000..c8a8848 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0016.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0017.tif b/stuff/projects/library/particles/snowflakerot.0017.tif new file mode 100644 index 0000000..8f5a8a6 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0017.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0018.tif b/stuff/projects/library/particles/snowflakerot.0018.tif new file mode 100644 index 0000000..68eacd0 Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0018.tif differ diff --git a/stuff/projects/library/particles/snowflakerot.0019.tif b/stuff/projects/library/particles/snowflakerot.0019.tif new file mode 100644 index 0000000..1c06f7f Binary files /dev/null and b/stuff/projects/library/particles/snowflakerot.0019.tif differ diff --git a/stuff/projects/library/particles/splash1.0001.rgb b/stuff/projects/library/particles/splash1.0001.rgb new file mode 100644 index 0000000..efcec44 Binary files /dev/null and b/stuff/projects/library/particles/splash1.0001.rgb differ diff --git a/stuff/projects/library/particles/splash1.0002.rgb b/stuff/projects/library/particles/splash1.0002.rgb new file mode 100644 index 0000000..7276988 Binary files /dev/null and b/stuff/projects/library/particles/splash1.0002.rgb differ diff --git a/stuff/projects/library/particles/splash1.0003.rgb b/stuff/projects/library/particles/splash1.0003.rgb new file mode 100644 index 0000000..7ad0f44 Binary files /dev/null and b/stuff/projects/library/particles/splash1.0003.rgb differ diff --git a/stuff/projects/library/particles/splash1.0004.rgb b/stuff/projects/library/particles/splash1.0004.rgb new file mode 100644 index 0000000..e37717f Binary files /dev/null and b/stuff/projects/library/particles/splash1.0004.rgb differ diff --git a/stuff/projects/library/particles/splash1.0005.rgb b/stuff/projects/library/particles/splash1.0005.rgb new file mode 100644 index 0000000..970c18a Binary files /dev/null and b/stuff/projects/library/particles/splash1.0005.rgb differ diff --git a/stuff/projects/library/particles/splash1.0006.rgb b/stuff/projects/library/particles/splash1.0006.rgb new file mode 100644 index 0000000..2a3ec2d Binary files /dev/null and b/stuff/projects/library/particles/splash1.0006.rgb differ diff --git a/stuff/projects/library/particles/splash1.0007.rgb b/stuff/projects/library/particles/splash1.0007.rgb new file mode 100644 index 0000000..6943ea3 Binary files /dev/null and b/stuff/projects/library/particles/splash1.0007.rgb differ diff --git a/stuff/projects/library/particles/splash1.0008.rgb b/stuff/projects/library/particles/splash1.0008.rgb new file mode 100644 index 0000000..f204e2f Binary files /dev/null and b/stuff/projects/library/particles/splash1.0008.rgb differ diff --git a/stuff/projects/library/particles/splash2.0001.rgb b/stuff/projects/library/particles/splash2.0001.rgb new file mode 100644 index 0000000..eb55bee Binary files /dev/null and b/stuff/projects/library/particles/splash2.0001.rgb differ diff --git a/stuff/projects/library/particles/splash2.0002.rgb b/stuff/projects/library/particles/splash2.0002.rgb new file mode 100644 index 0000000..d87e404 Binary files /dev/null and b/stuff/projects/library/particles/splash2.0002.rgb differ diff --git a/stuff/projects/library/particles/splash2.0003.rgb b/stuff/projects/library/particles/splash2.0003.rgb new file mode 100644 index 0000000..619938f Binary files /dev/null and b/stuff/projects/library/particles/splash2.0003.rgb differ diff --git a/stuff/projects/library/particles/splash2.0004.rgb b/stuff/projects/library/particles/splash2.0004.rgb new file mode 100644 index 0000000..78892ff Binary files /dev/null and b/stuff/projects/library/particles/splash2.0004.rgb differ diff --git a/stuff/projects/library/particles/splash2.0005.rgb b/stuff/projects/library/particles/splash2.0005.rgb new file mode 100644 index 0000000..5f05168 Binary files /dev/null and b/stuff/projects/library/particles/splash2.0005.rgb differ diff --git a/stuff/projects/library/particles/splash2.0006.rgb b/stuff/projects/library/particles/splash2.0006.rgb new file mode 100644 index 0000000..dbd41bd Binary files /dev/null and b/stuff/projects/library/particles/splash2.0006.rgb differ diff --git a/stuff/projects/library/particles/splash2.0007.rgb b/stuff/projects/library/particles/splash2.0007.rgb new file mode 100644 index 0000000..b8685b9 Binary files /dev/null and b/stuff/projects/library/particles/splash2.0007.rgb differ diff --git a/stuff/projects/library/particles/splash2.0008.rgb b/stuff/projects/library/particles/splash2.0008.rgb new file mode 100644 index 0000000..c7110d0 Binary files /dev/null and b/stuff/projects/library/particles/splash2.0008.rgb differ diff --git a/stuff/projects/library/particles/stain.0001.tif b/stuff/projects/library/particles/stain.0001.tif new file mode 100644 index 0000000..4344dba Binary files /dev/null and b/stuff/projects/library/particles/stain.0001.tif differ diff --git a/stuff/projects/library/particles/star.0001.tif b/stuff/projects/library/particles/star.0001.tif new file mode 100644 index 0000000..aa7adc8 Binary files /dev/null and b/stuff/projects/library/particles/star.0001.tif differ diff --git a/stuff/projects/library/particles/star.0002.tif b/stuff/projects/library/particles/star.0002.tif new file mode 100644 index 0000000..ab3f61b Binary files /dev/null and b/stuff/projects/library/particles/star.0002.tif differ diff --git a/stuff/projects/library/particles/star.0003.tif b/stuff/projects/library/particles/star.0003.tif new file mode 100644 index 0000000..70aaad8 Binary files /dev/null and b/stuff/projects/library/particles/star.0003.tif differ diff --git a/stuff/projects/library/particles/star.0004.tif b/stuff/projects/library/particles/star.0004.tif new file mode 100644 index 0000000..16dcf34 Binary files /dev/null and b/stuff/projects/library/particles/star.0004.tif differ diff --git a/stuff/projects/library/particles/star.0005.tif b/stuff/projects/library/particles/star.0005.tif new file mode 100644 index 0000000..bd100c9 Binary files /dev/null and b/stuff/projects/library/particles/star.0005.tif differ diff --git a/stuff/projects/library/particles/toothpaste.0001.tif b/stuff/projects/library/particles/toothpaste.0001.tif new file mode 100644 index 0000000..076608d Binary files /dev/null and b/stuff/projects/library/particles/toothpaste.0001.tif differ diff --git a/stuff/projects/library/shaders/caustics.xml b/stuff/projects/library/shaders/caustics.xml new file mode 100644 index 0000000..4457f72 --- /dev/null +++ b/stuff/projects/library/shaders/caustics.xml @@ -0,0 +1,21 @@ + + + SHADER_caustics + + + "programs/caustics.frag" + + + + + + rgba color + + 0 120 255 255 + + + + + float time + + \ No newline at end of file diff --git a/stuff/projects/library/shaders/fireball.xml b/stuff/projects/library/shaders/fireball.xml new file mode 100644 index 0000000..3cc314a --- /dev/null +++ b/stuff/projects/library/shaders/fireball.xml @@ -0,0 +1,38 @@ + + + SHADER_fireball + + + "programs/fireball.frag" + + + + + + rgba color1 + + 255 0 0 255 + + + + + rgba color2 + + 225 200 0 255 + + + + + float detail + + 12 + + + 0 10000 + + + + + float time + + diff --git a/stuff/projects/library/shaders/glitter.xml b/stuff/projects/library/shaders/glitter.xml new file mode 100644 index 0000000..8d8dda5 --- /dev/null +++ b/stuff/projects/library/shaders/glitter.xml @@ -0,0 +1,91 @@ + + + SHADER_glitter + + + "programs/glitter.frag" + + + + + + "Source" + + + + + SHADER_glitter_ports + + + "programs/glitter_ports.vert" + + + + + + + SHADER_glitter_bbox + + + "programs/glitter_bbox.vert" + + + + + Isotropic + + + + + float threshold + + 30 + + + 0 100 + + + + + float brightness + + 30 + + + 0 100 + + + + + float radius + + radius_ui + + + 5.333333333 + + + 0 10000 + + + + + float angle + + angle_ui + + + 45 + + + + + float halo + + 1 + + + 0 100 + + + \ No newline at end of file diff --git a/stuff/projects/library/shaders/programs/caustics.frag b/stuff/projects/library/shaders/programs/caustics.frag new file mode 100644 index 0000000..e1e04db --- /dev/null +++ b/stuff/projects/library/shaders/programs/caustics.frag @@ -0,0 +1,66 @@ +#ifdef GL_ES +precision mediump float; +#endif + +// Tweaked from http://glsl.heroku.com/e#6051.0 + + +// Posted by Trisomie21 : 2D noise experiment (pan/zoom) +// +// failed attempt at faking caustics +// + + +uniform mat3 outputToWorld; + +uniform vec4 color; +uniform float time; + + +vec4 textureRND2D(vec2 uv){ + uv = floor(uv); + float v = uv.x+uv.y*1e3; + + // Build space-specific corner values + vec4 res = fract(1e5*sin(vec4(v*1e-2, (v+1.)*1e-2, (v+1e3)*1e-2, (v+1e3+1.)*1e-2))); + + // Add 'sawtooth-like' wavefronts evolution + return 2.0 * abs(fract(res + vec4(time * .03)) - 0.5); +} + +float noise(vec2 p) { + vec4 r = textureRND2D(p); // Noise values at cell corners + + vec2 f = fract(p); + f = f*f*(3.0-2.0*f); // aka the smoothstep() builtin function + + return (mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y)); +} + +float buildColor(vec2 p) { + p += noise(p); // Noising p itself first. This helps + // preventing grid-like patterns. + + float v = 1.0 - abs(pow(abs(noise(p) - 0.5), 0.75)) * 1.7; // Lots of magical constants o_o? + return v; +} + + +const float SPEED = .15; + +void main( void ) { + vec2 p = (outputToWorld * vec3(gl_FragCoord.xy, 1.0)).xy; + + float c1 = buildColor(p*.03 + time * SPEED); + float c2 = buildColor(p*.03 - time * SPEED); + + float c3 = buildColor(p*.02 - time * SPEED); + float c4 = buildColor(p*.02 + time * SPEED); + + float cf = pow(c1*c2*c3*c4+0.5,6.); // Yep this is bad. Explicitly + // dependent on the 4 above. Better? + vec3 c = vec3(cf); + gl_FragColor = vec4(c, 0.0) + color; + + gl_FragColor.rgb *= gl_FragColor.a; // Premultiplication +} diff --git a/stuff/projects/library/shaders/programs/fireball.frag b/stuff/projects/library/shaders/programs/fireball.frag new file mode 100644 index 0000000..a5858fb --- /dev/null +++ b/stuff/projects/library/shaders/programs/fireball.frag @@ -0,0 +1,80 @@ +#ifdef GL_ES +precision mediump float; +#endif + +// Tweaked from http://glsl.heroku.com/e#5941.2 + +// +// Description : Array and textureless GLSL 2D/3D/4D +// noise functions with wrapping +// Author : People +// Maintainer : Anyone +// Lastmod : 20130111 (davidwparker) +// License : No Copyright No rights reserved. +// Freely distributed +// + + +uniform mat3 outputToWorld; + +uniform vec4 color1; +uniform vec4 color2; +uniform float detail; +uniform float time; + +const float pi_twice = 6.283185307; + + +float snoise(vec3 uv, float res) +{ + const vec3 s = vec3(1e0, 1e2, 1e4); + + uv *= res; + + vec3 uv0 = floor(mod(uv, res))*s; + vec3 uv1 = floor(mod(uv+vec3(1.), res))*s; + + vec3 f = fract(uv); + f = f*f*(3.0-2.0*f); + + vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z, + uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z); + + vec4 r = fract(sin(v*1e-3)*1e5); + float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y); + + r = fract(sin((v + uv1.z - uv0.z)*1e-3)*1e5); + float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y); + + return 2.0 * mix(r0, r1, f.z) - 1.0; // Range in [-1, 1] +} + +void main(void) +{ + vec2 p = .002 * (outputToWorld * vec3(gl_FragCoord.xy, 1.0)).xy; + + float color = 3.0 * (1.0 - 2.0 * length(p)); + vec3 coord = vec3(atan(p.y, p.x) / pi_twice, length(p) * 0.4, 0.0); + + for(int i = 1; i <= 7; i++) + { + float power = pow(2.0, float(i)); + vec3 timed = vec3(0.0, - time*.02, time*.01); + + color += 1.5 * snoise(coord + timed, power * detail) / power; + } + + color = max(color, 0.); + + + // ORIGINAL: + //gl_FragColor = vec4( color, pow(max(color,0.),2.)*0.4, pow(max(color,0.),3.)*0.15 , 1.0); + + vec4 col1 = color1 * color1.a, col2 = color2 * color2.a; + + gl_FragColor = mix(col1, col2, color / 3.0); + gl_FragColor.a *= smoothstep(0.0, 1.0, color); + + gl_FragColor.rgb *= gl_FragColor.a; // Premultiplication +} + diff --git a/stuff/projects/library/shaders/programs/glitter.frag b/stuff/projects/library/shaders/programs/glitter.frag new file mode 100644 index 0000000..51602b3 --- /dev/null +++ b/stuff/projects/library/shaders/programs/glitter.frag @@ -0,0 +1,121 @@ +#ifdef GL_ES +precision mediump float; +#endif + + +uniform mat3 worldToOutput; + +uniform sampler2D inputImage[1]; +uniform mat3 outputToInput[1]; + +mat3 worldToInput = outputToInput[0] * worldToOutput; + + +uniform float threshold; +uniform float brightness; +uniform float radius; +uniform float angle; +uniform float halo; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + +float scale = sqrt(abs(det(worldToOutput))); + + +float angle_ = radians(angle); +float sin_ = sin(angle_); +float cos_ = cos(angle_); +float threshold_ = 1.0 - 0.01 * threshold; + +float rad_ = radius * scale; + +const vec3 lVec = vec3(0.298980712, 0.587036132, 0.113983154); + +#define STEPS_PER_PIXEL 4.0 + +float stepsCount = ceil(STEPS_PER_PIXEL * rad_); + +float halo_ = 0.01 * (halo + 1.0) * stepsCount; + + +float rayWeight(float s) +{ + s /= halo_; + return clamp(1.0 - s * s, 0.0, 1.0); +} + +vec4 lightValue(const vec2 texCoord) +{ + vec4 col = texture2D(inputImage[0], texCoord); + float l = dot(lVec, col.rgb); + + return smoothstep(threshold_, 1.0, l) * col; +} + +bool filterLine(inout vec4 col, const vec2 p, const vec2 dx, float s_y) +{ + float rw = rayWeight(s_y); + if(rw == 0.0) + return false; + + float dw = max(1.0 - s_y / stepsCount, 0.0); + + col += dw * rw * lightValue(p); + + vec2 s = vec2(0.0, s_y); + + for(s.x = 1.0; s.x < stepsCount; s.x += 1.0) + { + dw = max(1.0 - length(s) / stepsCount, 0.0); + col += rw * dw * ( + lightValue(p + s.x * dx) + + lightValue(p - s.x * dx)); + } + + return true; +} + +void main( void ) +{ + vec2 texCoord = (outputToInput[0] * vec3(gl_FragCoord.xy, 1.0)).xy; + + float step = radius / stepsCount; + mat2 transf = + mat2((worldToInput * vec3(1.0, 0.0, 0.0)).xy, + (worldToInput * vec3(0.0, 1.0, 0.0)).xy) * // worldToInput without translational part + mat2(cos_, sin_, -sin_, cos_) * // angle shift by uniform parameter + step; // [-stepsCount,stepsCount]^2 to [-radius, radius]^2 + + + // Filter lines in the 2 orthogonal directions + vec4 addCol = vec4(0.0); + + // Horizontal + filterLine(addCol, texCoord, transf[0], 0.0); + + for(float s = 1.0; s < stepsCount; s += 1.0) + { + if(!filterLine(addCol, texCoord + s * transf[1], transf[0], s)) + break; + + filterLine(addCol, texCoord - s * transf[1], transf[0], s); + } + + // Vertical + filterLine(addCol, texCoord, transf[1], 0.0); + + for(float s = 1.0; s < stepsCount; s += 1.0) + { + if(!filterLine(addCol, texCoord + s * transf[0], transf[1], s)) + break; + + filterLine(addCol, texCoord - s * transf[0], transf[1], s); + } + + + float weight = stepsCount * STEPS_PER_PIXEL; + + vec4 col = texture2D(inputImage[0], texCoord); + gl_FragColor = col + addCol * (brightness / weight); +} diff --git a/stuff/projects/library/shaders/programs/glitter_bbox.vert b/stuff/projects/library/shaders/programs/glitter_bbox.vert new file mode 100644 index 0000000..5f6d331 --- /dev/null +++ b/stuff/projects/library/shaders/programs/glitter_bbox.vert @@ -0,0 +1,25 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform vec4 infiniteRect; +uniform vec4 inputBBox[1]; + +varying vec4 outputBBox; + +uniform float radius; + + +void main( void ) +{ + if(inputBBox[0] == infiniteRect) // Better avoid enlarging the infinite + outputBBox = infiniteRect; // rect... + else + outputBBox = vec4( + inputBBox[0].x - radius, + inputBBox[0].y - radius, + inputBBox[0].z + radius, + inputBBox[0].w + radius); + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/glitter_ports.vert b/stuff/projects/library/shaders/programs/glitter_ports.vert new file mode 100644 index 0000000..8fe07d5 --- /dev/null +++ b/stuff/projects/library/shaders/programs/glitter_ports.vert @@ -0,0 +1,29 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform mat3 worldToOutput; + +uniform vec4 outputRect; +varying vec4 inputRect[1]; +varying mat3 worldToInput[1]; + +uniform float radius; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + +void main( void ) +{ + float rad = radius * sqrt(abs(det(worldToOutput))); + + worldToInput[0] = worldToOutput; // Let the input and output references + // be the same + inputRect[0] = vec4( + outputRect.x - rad, + outputRect.y - rad, + outputRect.z + rad, + outputRect.w + rad); + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/radialblurGPU.frag b/stuff/projects/library/shaders/programs/radialblurGPU.frag new file mode 100644 index 0000000..b414fa7 --- /dev/null +++ b/stuff/projects/library/shaders/programs/radialblurGPU.frag @@ -0,0 +1,63 @@ +#ifdef GL_ES +precision mediump float; +#endif + + +uniform mat3 worldToOutput; + +uniform sampler2D inputImage[1]; +uniform mat3 outputToInput[1]; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + + +mat3 worldToInput = outputToInput[0] * worldToOutput; + +vec2 center_s = (worldToOutput * vec3(center, 1.0)).xy; +float scale_s = sqrt(abs(det(worldToOutput))); +float rad_s = scale_s * max(radius, 0.0); + + +#define STEPS_PER_PIXEL 4.0 + + +void main( void ) +{ + // Build lengths on output metrics + vec2 v = gl_FragCoord.xy - center_s; + float vLength = length(v); + + float dist_s = max(vLength - rad_s, 0.0); + float b_s = blur * dist_s; + + // Putting a maximum samples count - to prevent freezes; besides, blurring too many + // pixels is typically useless... + int samplesCount = int(clamp(ceil(b_s * STEPS_PER_PIXEL), 1.0, 2000.0)); + float step_s = b_s / float(samplesCount); + + + // Perform filtering + vec2 texPos = (outputToInput[0] * vec3(gl_FragCoord.xy, 1.0)).xy; + vec4 pix = texture2D(inputImage[0], texPos); + + vec2 vStep = v * (step_s / max(vLength, 0.01)); + vStep = (outputToInput[0] * vec3(vStep, 0.0)).xy; + + vec2 tPos0 = texPos + vStep; + vec2 tPos1 = texPos - vStep; + + for(int s = 1; s < samplesCount; ++s) + { + pix += texture2D(inputImage[0], tPos0); + pix += texture2D(inputImage[0], tPos1); + + tPos0 += vStep, tPos1 -= vStep; + } + + gl_FragColor = pix / float(2 * samplesCount - 1); +} diff --git a/stuff/projects/library/shaders/programs/radialblurGPU_bbox.vert b/stuff/projects/library/shaders/programs/radialblurGPU_bbox.vert new file mode 100644 index 0000000..730bdd8 --- /dev/null +++ b/stuff/projects/library/shaders/programs/radialblurGPU_bbox.vert @@ -0,0 +1,48 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform vec4 infiniteRect; +uniform vec4 inputBBox[1]; + +varying vec4 outputBBox; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +void addPoint(inout vec4 rect, vec2 p) { + rect.xy = min(rect.xy, p); + rect.zw = max(rect.zw, p); +} + +void addBlurredPointBox(inout vec4 rect, vec2 p) +{ + vec2 v = p - center; + float vLength = length(v); + + float dist = max(length(v) - radius, 0.0); + float b = blur * dist; + + v *= (b / max(vLength, 0.01)); + + addPoint(rect, p - v); + addPoint(rect, p + v); +} + +void main( void ) +{ + outputBBox = inputBBox[0]; + + if(outputBBox != infiniteRect) + { + // Add the bounding box of each blurred corner + addBlurredPointBox(outputBBox, inputBBox[0].xy); + addBlurredPointBox(outputBBox, inputBBox[0].xw); + addBlurredPointBox(outputBBox, inputBBox[0].zy); + addBlurredPointBox(outputBBox, inputBBox[0].zw); + } + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/radialblurGPU_ports.vert b/stuff/projects/library/shaders/programs/radialblurGPU_ports.vert new file mode 100644 index 0000000..2f0d8ab --- /dev/null +++ b/stuff/projects/library/shaders/programs/radialblurGPU_ports.vert @@ -0,0 +1,57 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform mat3 worldToOutput; + +uniform vec4 outputRect; + +varying vec4 inputRect[1]; +varying mat3 worldToInput[1]; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + + +float scale = sqrt(abs(det(worldToOutput))); + +vec2 center_ = (worldToOutput * vec3(center, 1.0)).xy; +float rad_ = scale * max(radius, 0.0); + + +void addPoint(inout vec4 rect, vec2 p) { + rect.xy = min(rect.xy, p); + rect.zw = max(rect.zw, p); +} + +void addBlurredPointBox(inout vec4 rect, vec2 p) +{ + vec2 v = p - center_; + float vLength = length(v); + + float dist = max(length(v) - rad_, 0.0); + float b = blur * dist; + + v *= (b / max(vLength, 0.01)); + + addPoint(rect, p - v); + addPoint(rect, p + v); +} + +void main( void ) +{ + worldToInput[0] = worldToOutput; // Let the input and output references be the same + inputRect[0] = outputRect; + + // Add the bounding box of each blurred corner + addBlurredPointBox(inputRect[0], outputRect.xy); + addBlurredPointBox(inputRect[0], outputRect.xw); + addBlurredPointBox(inputRect[0], outputRect.zy); + addBlurredPointBox(inputRect[0], outputRect.zw); + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/spinblurGPU.frag b/stuff/projects/library/shaders/programs/spinblurGPU.frag new file mode 100644 index 0000000..e28e979 --- /dev/null +++ b/stuff/projects/library/shaders/programs/spinblurGPU.frag @@ -0,0 +1,69 @@ +#ifdef GL_ES +precision mediump float; +#endif + + +uniform mat3 worldToOutput; + +uniform sampler2D inputImage[1]; +uniform mat3 outputToInput[1]; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + + +mat3 worldToInput = outputToInput[0] * worldToOutput; + +vec2 center_s = (worldToOutput * vec3(center, 1.0)).xy; +float scale_s = sqrt(abs(det(worldToOutput))); +float rad_s = scale_s * max(radius, 0.0); + + +#define STEPS_PER_PIXEL 4.0 + + +void main( void ) +{ + // Build lengths on output metrics + vec2 v = gl_FragCoord.xy - center_s; + float distance_s = length(v); + float angle = atan(v.y, v.x); + + float dist_s = max(distance_s - rad_s, 0.0); + float blurLen_s = radians(max(blur, 0.0)) * dist_s; + + float blur_ = blurLen_s / max(distance_s, 0.01); // Jump the singularity + + // Putting a maximum samples count - to prevent freezes; besides, blurring too many + // pixels is typically useless... + int samplesCount = int(clamp(ceil(blurLen_s * STEPS_PER_PIXEL), 1.0, 2000.0)); + + + float angle_step = blur_ / float(samplesCount); + + float cos_step = cos(angle_step); + float sin_step = sin(angle_step); + + mat2 rot_step0 = mat2(cos_step, sin_step, -sin_step, cos_step); + mat2 rot_step1 = mat2(cos_step, -sin_step, sin_step, cos_step); + + + // Perform filtering + vec4 pix = texture2D(inputImage[0], (outputToInput[0] * vec3(gl_FragCoord.xy, 1.0)).xy); + + vec2 v0 = rot_step0 * v, v1 = rot_step1 * v; + + for(int s = 1; s < samplesCount; ++s) + { + pix += texture2D(inputImage[0], (outputToInput[0] * vec3(center_s + v0, 1.0)).xy); + pix += texture2D(inputImage[0], (outputToInput[0] * vec3(center_s + v1, 1.0)).xy); + + v0 = rot_step0 * v0, v1 = rot_step1 * v1; + } + + gl_FragColor = pix / float(2 * samplesCount - 1); +} diff --git a/stuff/projects/library/shaders/programs/spinblurGPU_bbox.vert b/stuff/projects/library/shaders/programs/spinblurGPU_bbox.vert new file mode 100644 index 0000000..106a466 --- /dev/null +++ b/stuff/projects/library/shaders/programs/spinblurGPU_bbox.vert @@ -0,0 +1,73 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform vec4 infiniteRect; +uniform vec4 inputBBox[1]; + +varying vec4 outputBBox; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +const float pi = 3.141592653; +const float pi_twice = 2.0 * pi; +const float pi_half = pi / 2.0; + + +void addPoint(inout vec4 rect, vec2 p) { + rect.xy = min(rect.xy, p.xy); + rect.zw = max(rect.zw, p.xy); +} + +void addBlurredPointBox(inout vec4 rect, vec2 p) +{ + // Remember the *definition* of angle: angle = arc length / radius + + // Build p's blurred angular range + float distance = length(p - center); + float angle = atan(p.y - center.y, p.x - center.x); + + // If radius > 0, we assume that the blurred length is proportional to (distance - radius); + float dist_ = max(distance - radius, 0.0); + float blurLen = radians(max(blur, 0.0)) * dist_; + + // The actual blurring angle is then found as (blurLen_ / distance) + float blur_ = blurLen / max(distance, 0.01); // Jump the singularity + + vec2 angleRange = vec2(angle - blur_, angle + blur_); // Couldn't make it an array with + // explicit initialization... GLSL complained -.- + // Include the points at angleRange's extremes + addPoint(rect, center + distance * vec2(cos(angleRange.x), sin(angleRange.x))); + addPoint(rect, center + distance * vec2(cos(angleRange.y), sin(angleRange.y))); + + // At pi/2 multiples we get a box extreme. Include them if present. + float blur_twice = 2.0 * blur_; + + if(mod( - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center + vec2(distance, 0.0)); + if(mod(pi_half - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center + vec2(0.0, distance)); + if(mod(pi - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center + vec2(-distance, 0.0)); + if(mod(-pi_half - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center + vec2(0.0, -distance)); +} + +void main( void ) +{ + outputBBox = inputBBox[0]; + + if(outputBBox != infiniteRect) + { + // Add the bounding box of each blurred corner + addBlurredPointBox(outputBBox, inputBBox[0].xy); + addBlurredPointBox(outputBBox, inputBBox[0].xw); + addBlurredPointBox(outputBBox, inputBBox[0].zy); + addBlurredPointBox(outputBBox, inputBBox[0].zw); + } + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/spinblurGPU_ports.vert b/stuff/projects/library/shaders/programs/spinblurGPU_ports.vert new file mode 100644 index 0000000..f755b29 --- /dev/null +++ b/stuff/projects/library/shaders/programs/spinblurGPU_ports.vert @@ -0,0 +1,81 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform mat3 worldToOutput; + +uniform vec4 outputRect; + +varying vec4 inputRect[1]; +varying mat3 worldToInput[1]; + +uniform vec2 center; +uniform float radius; +uniform float blur; + + +float det(mat3 m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } + + +float scale = sqrt(abs(det(worldToOutput))); + +vec2 center_ = (worldToOutput * vec3(center, 1.0)).xy; +float rad_ = scale * max(radius, 0.0); + +const float pi = 3.141592653; +const float pi_twice = 2.0 * pi; +const float pi_half = pi / 2.0; + + +void addPoint(inout vec4 rect, vec2 p) { + rect.xy = min(rect.xy, p.xy); + rect.zw = max(rect.zw, p.xy); +} + +void addBlurredPointBox(inout vec4 rect, vec2 p) +{ + // Remember the *definition* of angle: angle = arc length / radius + + // Build p's blurred angular range + float distance = length(p - center_); + float angle = atan(p.y - center_.y, p.x - center_.x); + + // If rad_ > 0, we assume that the blurred length is proportional to (distance - rad_); + float dist_ = max(distance - rad_, 0.0); + float blurLen = radians(max(blur, 0.0)) * dist_; + + // The actual blurring angle is then found as (blurLen_ / distance) + float blur_ = blurLen / max(distance, 0.01); // Jump the singularity + + vec2 angleRange = vec2(angle - blur_, angle + blur_); // Couldn't make it an array with + // explicit initialization... GLSL complained -.- + // Include the points at angleRange's extremes + addPoint(rect, center_ + distance * vec2(cos(angleRange.x), sin(angleRange.x))); + addPoint(rect, center_ + distance * vec2(cos(angleRange.y), sin(angleRange.y))); + + // At pi/2 multiples we get a box extreme. Include them if present. + float blur_twice = 2.0 * blur_; + + if(mod( - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center_ + vec2(distance, 0.0)); + if(mod(pi_half - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center_ + vec2(0.0, distance)); + if(mod(pi - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center_ + vec2(-distance, 0.0)); + if(mod(-pi_half - angleRange.x, pi_twice) < blur_twice) + addPoint(rect, center_ + vec2(0.0, -distance)); +} + +void main( void ) +{ + worldToInput[0] = worldToOutput; // Let the input and output references be the same + inputRect[0] = outputRect; + + // Add the bounding box of each blurred corner + addBlurredPointBox(inputRect[0], outputRect.xy); + addBlurredPointBox(inputRect[0], outputRect.xw); + addBlurredPointBox(inputRect[0], outputRect.zy); + addBlurredPointBox(inputRect[0], outputRect.zw); + + gl_Position = vec4(0.0); // Does not link without +} diff --git a/stuff/projects/library/shaders/programs/starsky.frag b/stuff/projects/library/shaders/programs/starsky.frag new file mode 100644 index 0000000..bb45db2 --- /dev/null +++ b/stuff/projects/library/shaders/programs/starsky.frag @@ -0,0 +1,85 @@ +#ifdef GL_ES +precision mediump float; +#endif + +// Tweaked from http://glsl.heroku.com/e#6015.0 + + +// Posted by Trisomie21 + + +uniform mat3 outputToWorld; + +uniform vec4 color; +uniform float time; +uniform float brightness; + +// Tweaked from http://glsl.heroku.com/e#4982.0 +float hash( float n ) { return fract(sin(n)*43758.5453); } +float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } + +float noise( in vec2 x ) +{ + vec2 p = floor(x); + vec2 f = fract(x); + f = f*f*(3.0-2.0*f); + float n = p.x + p.y*57.0; + float res = mix(mix(hash(n+0.0), hash(n+1.0),f.x), mix(hash(n+57.0), hash(n+58.0),f.x),f.y); + return res; +} + +vec3 cloud(vec2 p) { + float f = 0.0; + f += 0.50000*noise(p*1.0*10.0); + f += 0.25000*noise(p*2.0*10.0); + f += 0.12500*noise(p*4.0*10.0); + f += 0.06250*noise(p*8.0*10.0); + f *= f; + + return color.rgb * color.a * f * .6; +} + +const float SPEED = 0.01; +const float DENSITY = 1.5; + +void main( void ) +{ + vec2 pos = .01 * (outputToWorld * vec3(gl_FragCoord.xy, 1.0)).xy; + + // Nebulous cloud - It's intended as background, ie it doesn't block stars visibility. + // Stars ADD to this. + vec3 color = cloud(pos); + + // Stars Field - this is the idea: each star is drawn in a 'star cell' which results from + // FLOORING a point function p(x,y) of the pixel coordinates. A cell's edges correspond to + // coordinated lines of the form: p_x(x,y) = int, p_y(x, y) = int. + + // The problem lies in finding a function p which is suitable, ie p_x's and p_y's gradients should + // be as orthogonal and with finite strictly positive norm as possible. + + // Changing to polar coordinates is simplest - when the radius (distance from origin) + // is high, moving in radius and arc distance is almost orthogonal. Plus, star 'discs' are harder + // to spot than star 'rows', since they are curved. + + // I think that a suitable deformation of the identity grid based on sin and cos exists, + // but couldn't find it... ^.^' + + float dist = length(pos); + vec2 coord = vec2(dist, atan(pos.y, pos.x)/* / (3.1415926*2.0)*/); // Pseudo-polar coordinates + + vec2 p = 40.0 * vec2(coord.x, // radius + floor(coord.x + 1.0) * coord.y + // arc distance (floor helps stabilizing cell shapes, and 1.0 to avoid flooring to 0) + hash(floor(40.0 * coord.x))); // shifts the star 'discs' along the arc, by a pseudo-random value (helps avoiding 'star rows', at least along the radial direction) + + vec2 uv = 2.0 * fract(p) - 1.0; // Pixel position in the cell, in [-1,1]^2 coordinates + + float cellValue = abs(2.0 * fract(rand(floor(p)) + SPEED * time) - 1.0); + float cellBrightness = clamp((cellValue - 0.9) * brightness * 10.0, 0.0, 1.0); + + color += clamp( + (1.0 - 2.0 * length(uv)) * // Comment this line to see the star cells + cellBrightness, 0.0, 1.0); + + + gl_FragColor = vec4(color, 1.0); +} diff --git a/stuff/projects/library/shaders/programs/sunflare.frag b/stuff/projects/library/shaders/programs/sunflare.frag new file mode 100644 index 0000000..4c8f361 --- /dev/null +++ b/stuff/projects/library/shaders/programs/sunflare.frag @@ -0,0 +1,31 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform mat3 outputToWorld; + +uniform vec4 color; +uniform int blades; +uniform float intensity; +uniform float angle; +uniform float bias; +uniform float sharpness; + +float blades_ = float(blades); +float angle_ = radians(angle); +float bias_ = .01 * bias; + + +// never watch into the sun ;) + +void main( void ) +{ + vec2 p = .03 * (outputToWorld * vec3(gl_FragCoord.xy, 1.0)).xy; + + float a = atan(p.y, p.x) - angle_; + float blade = intensity * clamp(pow(sin(a * blades_) + bias_, sharpness), 0.0, 1.0); + + gl_FragColor = vec4(color.rgb * color.a, color.a); // Premultiplication + gl_FragColor = gl_FragColor * (1.0 + blade) / length(p); +} + diff --git a/stuff/projects/library/shaders/programs/wavy.frag b/stuff/projects/library/shaders/programs/wavy.frag new file mode 100644 index 0000000..08b1476 --- /dev/null +++ b/stuff/projects/library/shaders/programs/wavy.frag @@ -0,0 +1,71 @@ +#ifdef GL_ES +precision mediump float; +#endif + +// Tweaked from http://glsl.heroku.com/e#5893.0 + + +uniform mat3 outputToWorld; + +uniform vec4 color1; +uniform vec4 color2; +uniform float time; + +vec2 Distort(vec2 p) +{ + float theta = atan(p.y, p.x); + float radius = length(p); + radius = pow(radius, 1.3); + p.x = radius * cos(theta); + p.y = radius * sin(theta); + return 0.5 * (p + 1.0); +} +vec4 pattern(vec2 p) +{ + vec2 m=mod(p.xy+p.x+p.y,2.)-1.; + return vec4(length(m)); +} + +float hash(const float n) +{ + return fract(sin(n)*43758.5453); +} + +float noise(const vec3 x) +{ + vec3 p=floor(x); + vec3 f=fract(x); + + f=f*f*(3.0-2.0*f); + + float n=p.x+p.y*57.0+p.z*43.0; + + float r1=mix(mix(hash(n+0.0),hash(n+1.0),f.x),mix(hash(n+57.0),hash(n+57.0+1.0),f.x),f.y); + float r2=mix(mix(hash(n+43.0),hash(n+43.0+1.0),f.x),mix(hash(n+43.0+57.0),hash(n+43.0+57.0+1.0),f.x),f.y); + + return mix(r1,r2,f.z); +} + +void main( void ) +{ + vec2 position = .01 * (outputToWorld * vec3(gl_FragCoord.xy, 1.0)).xy; + + float off = noise(position.xyx + time); + vec4 c = pattern(Distort(position+off)); + + c.xy = Distort(c.xy); + + + // ORIGINAL: + // vec4(c.x - off, sin(c.y) - off, cos(c.z), 1.0); + + // The original green component did not show much. So, the original formula can be written + // as a linear combination of those R and B channels - we generalize that to 2 arbitrary + // colors. Plus, the resulting color is required to be in a premultiplied form. + + vec4 col1 = vec4(color1.rgb * color1.a, color1.a); // Premultiplication + vec4 col2 = vec4(color2.rgb * color2.a, color2.a); // + + float coeff1 = c.x - off, coeff2 = cos(c.z); + gl_FragColor = (coeff1 * col1 + coeff2 * col2) / (coeff1 + coeff2); +} \ No newline at end of file diff --git a/stuff/projects/library/shaders/radialblurGPU.xml b/stuff/projects/library/shaders/radialblurGPU.xml new file mode 100644 index 0000000..b24bb65 --- /dev/null +++ b/stuff/projects/library/shaders/radialblurGPU.xml @@ -0,0 +1,87 @@ + + + SHADER_radialblurGPU + + + "programs/radialblurGPU.frag" + + + + + + "Source" + + + + + SHADER_radialblurGPU_ports + + + "programs/radialblurGPU_ports.vert" + + + + + + + SHADER_radialblurGPU_bbox + + + "programs/radialblurGPU_bbox.vert" + + + + + isotropic + + + + + vec2 center + + point_ui + + Center + + + + + + float radius + + length + + + 3 + + + 0 10000 + + + + + float blur + + percent + + + 0.3 + + + 0 10000 + + + + + + radius_ui + + Radius + + + radius + + + center + + diff --git a/stuff/projects/library/shaders/readme.txt b/stuff/projects/library/shaders/readme.txt new file mode 100644 index 0000000..ba3b1ea --- /dev/null +++ b/stuff/projects/library/shaders/readme.txt @@ -0,0 +1,462 @@ + + Toonz Shader Fxs Manual + +========================================================== + + 1. Introduction + + +Toonz 7.1 allows users to write new Fxs using GLSL (the +OpenGL Shading Language). + +Shader Fx interfaces are read once at Toonz's startup, +but the underlying fx algorithm can be modified in +real time to ease the fx creation process. + + +Users reading these notes for the first time may want to +refer to the official GLSL guide at: + + http://www.opengl.org/documentation/glsl/ + +Up-and-running examples of GLSL (fragment) shader programs +can be found at the GLSL sanbox gallery, from which some of +the provided examples are adapted from (requires a +WebGL-compatible web browser, such as Firefox or Google +Chrome): + + http://glsl.heroku.com/ + +Further examples can be found at the beautiful gallery at: + + https://www.shadertoy.com/ + +========================================================== + + 2. Requirements + + +The most recent version of your graphics drivers, as well +as a fairly recent graphics card. + +Specifically, graphics drivers must support OpenGL 2.1, +Transform Feedback and Pixel Buffers (either as a built-in +feature or through extensions). + +========================================================== + + 3. Limitations + + +Shader fxs are rendered on the GPU, meaning that they are +typically executed in a massively parallel fashion - ie fast. + +However, since most systems only adopt one GPU, only one +Shader fx is allowed to be rendered at the same time. +This means that Shader Fxs do not take advantage of multiple +rendering threads in a Toonz rendering process like common +CPU-based fxs do. + + +Shader Fx are intended to apply a fragment shader on the +output surface for the fx. In other words, each output pixel +is processed separately using the supplied fragment shader +program. + +This prevents the implementation of more complex output +patterns that span multiple pixels at the same time. + +Furthermore, there is no way to specify intermediate buffer +objects to read or write data to - which is often a common +need when writing fxs. + +========================================================== + + 3. Implementing a Shader Fx + + +In order to implement a shader fx it's currently necessary +to either create or edit the following files: + + a. /config/current.txt + + This file hosts the associations between fxs and + their parameters and the names displayed in the GUI + (which are not locale-dependent). + + b. /profiles/layouts/fxs/fxs.lst + + The list of fxs as displayed in the right-click + contextual menus like "Add Fx" or "Insert Fx" + + c. /profiles/layouts/fxs/.xml + + Parameters tabbing in the Fx Parameters Editor + + d. /shaders/.xml + + The Shader Fx interface. + + e. The actual shader program files + + +Please, observe that the paths and names outside brackets +are mandatory. + +Apart from point (d) and (e) discussed separately, it is best +to locate existing entries and emulate their behavior. +You can typically find related entries by searching "Shader" +in each file. + +========================================================== + + 4. The Shader Interface File + + +The Shader Fx Interface file at (3.d) is an xml document that +defines the main properties of the fx. + +Specifically: + + a. Shader program files to be compiled at run-time + + b. Input ports for the fx + + c. Parameters + + d. Restrictions to the class of world/output coordinates + transforms handled by the fx + +The file is read once when Toonz starts, so any modification +will not be recognized until Toonz is restarted. + + +The complete recognized file structure is as follows: + + + // (4.a) The applied fragment shader + + SHADER_myShaderName // Internal name of the fx (mandatory, a simple app-unique literal id) + + + "programs/myShader.frag" // The shader program file (3.e), relative to the + // path of the interface file. + + + // (4.b) - Only a *fixed* number of ports allowed + // A first port + "Source" // The displayed port name + + + // Second port + "Control" + + + // (4.a) Vertex shader used to acquire the geometry of + // input images. See (5.b). + SHADER_myShader_ports // The unique id for the vertex shader program (mandatory) + + + "programs/myShader_ports.vert" + + + + + // (4.a) Vertex shader used to calculate the fx's bbox. + // See (5.c). + SHADER_myShader_bbox + + + "programs/myShader_bbox.vert" + + + + // (4.d) Optional, see (5.a) + isotropic // May be either 'any' (default) or 'isotropic'. + // Isotropic transforms exclude shears and non-uniform scales. + + // (4.c) + + + float radius // Parameter declaration + + // Additional Paramater attributes (can be omitted) + 10 // The parameter default + + + 0 20 // The parameter range + + + length // The parameter concept type - or, how it is represented + // by the Toonz GUI + + + + + float angle + + + angle_ui // Concepts of type _ui are editable in + // camera stand + "My Angle" + + + + + + // Composite parameter concepts can be formed by 2 or + // more parameters + polar_ui + + + "My Polar Coordinates" + + // List of involved parameters + radius + + + angle + + + +---------------------------------------------------------- + +4.1. Parameter Declarations + + +Parameters are introduced by a declaration typically matching +the corresponding GLSL variable declaration. + +The complete recognized list of supported parameter types is: + + bool, float, vec2, int, ivec2, rgb, rgba + + +The 'rgb' and 'rgba' types map to GLSL 'vec3' and 'vec4' +variables respectively, but are displayed with the appropriate +color editors by Toonz - plus, the range of their components +automatically maps from [0, 255] in Toonz and the Shader +Interface file to [0.0, 1.0] in the corresponding shader program +files. + +---------------------------------------------------------- + +4.2. Parameter Concepts + + +Parameter 'concepts' are additional parameter properties that +regard the way Toonz represents a certain parameter type. + +For example, a 'float' variable type may either indicate +an angle, the length of a segment, a percentage value, +and more. + +Fx writers may want to explicitly specify a parameter concept +for the following reasons: + + a. Impose a measure to the parameter (e.g. degress, inches, %) + + b. Make the parameter editable in camera-stand + + +The complete list of supported parameter concepts is the following: + + percent - Displayed with the percentage '%' unit + + length - Displayed in length units (inches, mm, cm, etc..) + + angle - Displayed in angular units '�' + + point - A vec2 displayed in length units + + radius_ui - Like length, displaying a radius in camstand. May compose with a point (the center) + + width_ui - Like length, displaying a vertical line width. May compose with the line's angle. + + angle_ui - Like angle, displaying it in camstand + + point_ui - Like point, in camstand + + xy_ui - Composes two float types in a point + + vector_ui - Composes two float types in an 'arrow'-like vector + + polar_ui - Like vector_ui, from a length and an angle + + size_ui - Displays a square indicating a size. May compose width and height in a rect. + + quad_ui - Composes 4 points in a quadrilateral + + rect_ui - Composes width, height, and the optional center point in a rect + +========================================================== + + 5. Shader program files + + +A shader program file is a simple text file containing the +actual algorithms of a shader fx. + +In the current implementation of Toonz Shader Fxs, there are +3 possible shader program files that need to be specified: + + a. The main fragment shader program, responsible of + executing the code that actually renders the fx + + b. An optional vertex shader program to calculate the + geometries of contents required from input ports + + c. An optional vertex shader program to calculate the + bounding box of the fx output + +---------------------------------------------------------- + + 5.a. The 'MainProgram' Fragment Shader + + +The main program is in practice a standard GLSL fragment +shader - however, Toonz will provide it a set of additional +uniform input variables that must be addressed to correctly +compute the desired output. + + +The complete list of additional variables always supplied +by Toonz is: + + uniform mat3 worldToOutput; + uniform mat3 outputToWorld; + +These matrix variables describe the affine transforms mapping +output coordinates to Toonz's world coordinates, and vice-versa. + +They include an additional coordinate as an OpenGL version-portable +way to perform translations by natural multiplication - transforming +a point is then done like: + + vec2 worldPoint = (outputToWorld * vec3(outPoint, 1.0)).xy + +Fx parameters are typically intended in world coordinates, +and should be adjusted through these transforms - for example, +a camstand-displayed radius value must be multiplied by the +'worldToOutput' scale factors in order to get the corresponding +value in output coordinates. + +World/Output transforms may be restricted to a specific sub-class +of affine transforms by specifying so in the Shader Interface File. + +Restricting to isotropic transforms may be useful to simplify +cases where angular values are taken into account, since this +transforms class preserves angles by allowing only uniform scales, +rotations and translations. Non-uniform scales and shears are +later applied by Toonz on the produced fx output if necessary. + + +In case input ports have been specified, we also have: + + uniform sampler2D inputImage[n]; + uniform mat3 outputToInput[n]; + uniform mat3 inputToOutput[n]; + +The sampler variables correspond to the input content to the +fx. The matrix variables are the reference transforms from +output to input variables, and vice-versa. + + +Additional uniform variables corresponding to fx parameters +will also be supplied by Toonz. For example, if a "float radius" +parameter was specified, a corresponding + + uniform float radius; + +input variable will be provided to the program. + + +WARNING: Toonz requires that *output* colors must be + 'premultiplied' - that is, common RGB components + (in the range [0, 1]) must be stored multiplied + by their alpha component. + +---------------------------------------------------------- + + 5.b. The optional 'PortsProgram' Vertex Shader + + +The shader program (b) is required in case an fx specifies +input ports, AND it needs to calculate some input content +in a different region than the required output. +It can be neglected otherwise. + +For example, a blur fx requires that input contents outside +the required output rectangle are 'blurred in' it. + + +The 'PortsProgram' vertex shader is a one-shot shader +run by Toonz on a single dummy vertex - which uses +OpenGL 3.0's "Transform Feedback" extension to return a +set of predefined 'varying' output variables + + +The complete set of variables supplied by Toonz and required +in output by the program is: + + uniform mat3 worldToOutput; + uniform mat3 outputToWorld; + uniform vec4 outputRect; + + varying vec4 inputRect[portsCount]; + varying vec4 worldToInput[portsCount]; + +The transforms are intended in the same way as (5.a). + +The outputRect and inputRect[] variables store the +(left, bottom, right, top) rect components in output +and input coordinates respectively. + +Parameter input variables are obviously also supplied. + + +WARNING: *All* the required output variables must be + declared AND filled with values. + + There is no recognized default for them, and the + fx will (silently) fail to render if some are not + assigned. + +---------------------------------------------------------- + + 5.c. The optional 'BBoxProgram' Vertex Shader + + +Some fx may be able to restrict their opaque renderable +area inside a rect. + +For example, blurring an image will 'blur out' the image +content by the specified blur radius. Beyond that, the fx +will render full transparent pixels. Thus, the bounding +box of the fx in this case will be calculated as the +input bounding box, enlarged by the blur radius. + +The default output bounding box is assumed to be infinite; +if that is the case, the BBoxProgram can be omitted. + + +Fx writers may want to supply an explicit program to +calculate the bounding box of the fx, given its input +bounding boxes. This is be useful in Toonz's rendering +pipeline because the software is then allowed to +restrict memory allocation (and fxs calculations) +for the output image to said output bounding box, resulting +in less memory consumption and increased speed. + + +The complete set of variables supplied by Toonz and required +in output by the program is: + + uniform vec4 infiniteRect; + uniform vec4 inputBBox[portsCount]; + + varying vec4 outputBBox; + +The infiniteRect variable should be used to identify both +input and output infinite bboxes. + diff --git a/stuff/projects/library/shaders/spinblurGPU.xml b/stuff/projects/library/shaders/spinblurGPU.xml new file mode 100644 index 0000000..57df18a --- /dev/null +++ b/stuff/projects/library/shaders/spinblurGPU.xml @@ -0,0 +1,87 @@ + + + SHADER_spinblurGPU + + + "programs/spinblurGPU.frag" + + + + + + "Source" + + + + + SHADER_spinblurGPU_ports + + + "programs/spinblurGPU_ports.vert" + + + + + + + SHADER_spinblurGPU_bbox + + + "programs/spinblurGPU_bbox.vert" + + + + + isotropic + + + + + vec2 center + + point_ui + + Center + + + + + + float radius + + length + + + 3 + + + 0 10000 + + + + + float blur + + angle + + + 1 + + + 0 180 + + + + + + radius_ui + + Radius + + + radius + + + center + + \ No newline at end of file diff --git a/stuff/projects/library/shaders/starsky.xml b/stuff/projects/library/shaders/starsky.xml new file mode 100644 index 0000000..4383222 --- /dev/null +++ b/stuff/projects/library/shaders/starsky.xml @@ -0,0 +1,29 @@ + + + SHADER_starsky + + + "programs/starsky.frag" + + + + + + rgba color + + 128 0 255 255 + + + + float time + + + float brightness + + 1 + + + 0 10 + + + \ No newline at end of file diff --git a/stuff/projects/library/shaders/sunflare.xml b/stuff/projects/library/shaders/sunflare.xml new file mode 100644 index 0000000..f0811d5 --- /dev/null +++ b/stuff/projects/library/shaders/sunflare.xml @@ -0,0 +1,61 @@ + + + SHADER_sunflare + + + "programs/sunflare.frag" + + + + + + rgba color + + 255 170 75 255 + + + + + float angle + + angle_ui + + + + + int blades + + 6 + + + 0 100 + + + + + float intensity + + 1 + + + 0 10000 + + + + + float bias + + 0 + + + -100 100 + + + + + float sharpness + + 3.0 + + + \ No newline at end of file diff --git a/stuff/projects/library/shaders/wavy.xml b/stuff/projects/library/shaders/wavy.xml new file mode 100644 index 0000000..2a6b72c --- /dev/null +++ b/stuff/projects/library/shaders/wavy.xml @@ -0,0 +1,28 @@ + + + SHADER_wavy + + + "programs/wavy.frag" + + + + + + rgba color1 + + 0 0 255 255 + + + + + rgba color2 + + 255 0 0 255 + + + + + float time + + \ No newline at end of file diff --git a/stuff/projects/library/textures/Denim2_s.bmp b/stuff/projects/library/textures/Denim2_s.bmp new file mode 100644 index 0000000..b1813aa Binary files /dev/null and b/stuff/projects/library/textures/Denim2_s.bmp differ diff --git a/stuff/projects/library/textures/Knit_s.bmp b/stuff/projects/library/textures/Knit_s.bmp new file mode 100644 index 0000000..d7f9b7e Binary files /dev/null and b/stuff/projects/library/textures/Knit_s.bmp differ diff --git a/stuff/projects/library/textures/Paper 1.BMP b/stuff/projects/library/textures/Paper 1.BMP new file mode 100644 index 0000000..47454ff Binary files /dev/null and b/stuff/projects/library/textures/Paper 1.BMP differ diff --git a/stuff/projects/library/textures/Paper 4.BMP b/stuff/projects/library/textures/Paper 4.BMP new file mode 100644 index 0000000..3ec8711 Binary files /dev/null and b/stuff/projects/library/textures/Paper 4.BMP differ diff --git a/stuff/projects/library/textures/Thumbs.db b/stuff/projects/library/textures/Thumbs.db new file mode 100644 index 0000000..be9208d Binary files /dev/null and b/stuff/projects/library/textures/Thumbs.db differ diff --git a/stuff/projects/library/textures/arabesque.bmp b/stuff/projects/library/textures/arabesque.bmp new file mode 100644 index 0000000..57ffc28 Binary files /dev/null and b/stuff/projects/library/textures/arabesque.bmp differ diff --git a/stuff/projects/library/textures/brickwork.bmp b/stuff/projects/library/textures/brickwork.bmp new file mode 100644 index 0000000..414a700 Binary files /dev/null and b/stuff/projects/library/textures/brickwork.bmp differ diff --git a/stuff/projects/library/textures/carpet.bmp b/stuff/projects/library/textures/carpet.bmp new file mode 100644 index 0000000..9df2d0b Binary files /dev/null and b/stuff/projects/library/textures/carpet.bmp differ diff --git a/stuff/projects/library/textures/chessboard.bmp b/stuff/projects/library/textures/chessboard.bmp new file mode 100644 index 0000000..e8b1a33 Binary files /dev/null and b/stuff/projects/library/textures/chessboard.bmp differ diff --git a/stuff/projects/library/textures/clouds.bmp b/stuff/projects/library/textures/clouds.bmp new file mode 100644 index 0000000..d08a30d Binary files /dev/null and b/stuff/projects/library/textures/clouds.bmp differ diff --git a/stuff/projects/library/textures/drystonewall.bmp b/stuff/projects/library/textures/drystonewall.bmp new file mode 100644 index 0000000..b791cf7 Binary files /dev/null and b/stuff/projects/library/textures/drystonewall.bmp differ diff --git a/stuff/projects/library/textures/flame.bmp b/stuff/projects/library/textures/flame.bmp new file mode 100644 index 0000000..5360872 Binary files /dev/null and b/stuff/projects/library/textures/flame.bmp differ diff --git a/stuff/projects/library/textures/gold.bmp b/stuff/projects/library/textures/gold.bmp new file mode 100644 index 0000000..6b8acf1 Binary files /dev/null and b/stuff/projects/library/textures/gold.bmp differ diff --git a/stuff/projects/library/textures/grass.bmp b/stuff/projects/library/textures/grass.bmp new file mode 100644 index 0000000..2c8f6d7 Binary files /dev/null and b/stuff/projects/library/textures/grass.bmp differ diff --git a/stuff/projects/library/textures/ironware.bmp b/stuff/projects/library/textures/ironware.bmp new file mode 100644 index 0000000..ce902a8 Binary files /dev/null and b/stuff/projects/library/textures/ironware.bmp differ diff --git a/stuff/projects/library/textures/kilt.bmp b/stuff/projects/library/textures/kilt.bmp new file mode 100644 index 0000000..76cb661 Binary files /dev/null and b/stuff/projects/library/textures/kilt.bmp differ diff --git a/stuff/projects/library/textures/leaves.bmp b/stuff/projects/library/textures/leaves.bmp new file mode 100644 index 0000000..73fe206 Binary files /dev/null and b/stuff/projects/library/textures/leaves.bmp differ diff --git a/stuff/projects/library/textures/marble.bmp b/stuff/projects/library/textures/marble.bmp new file mode 100644 index 0000000..cb410d4 Binary files /dev/null and b/stuff/projects/library/textures/marble.bmp differ diff --git a/stuff/projects/library/textures/papercrump.bmp b/stuff/projects/library/textures/papercrump.bmp new file mode 100644 index 0000000..7b6f039 Binary files /dev/null and b/stuff/projects/library/textures/papercrump.bmp differ diff --git a/stuff/projects/library/textures/pearl.bmp b/stuff/projects/library/textures/pearl.bmp new file mode 100644 index 0000000..2ec2d69 Binary files /dev/null and b/stuff/projects/library/textures/pearl.bmp differ diff --git a/stuff/projects/library/textures/piastrella.bmp b/stuff/projects/library/textures/piastrella.bmp new file mode 100644 index 0000000..d5afd9d Binary files /dev/null and b/stuff/projects/library/textures/piastrella.bmp differ diff --git a/stuff/projects/library/textures/pool.bmp b/stuff/projects/library/textures/pool.bmp new file mode 100644 index 0000000..61863a3 Binary files /dev/null and b/stuff/projects/library/textures/pool.bmp differ diff --git a/stuff/projects/library/textures/rag_tile_s copy.bmp b/stuff/projects/library/textures/rag_tile_s copy.bmp new file mode 100644 index 0000000..c37f47e Binary files /dev/null and b/stuff/projects/library/textures/rag_tile_s copy.bmp differ diff --git a/stuff/projects/library/textures/roughbrickwork.bmp b/stuff/projects/library/textures/roughbrickwork.bmp new file mode 100644 index 0000000..74e6e36 Binary files /dev/null and b/stuff/projects/library/textures/roughbrickwork.bmp differ diff --git a/stuff/projects/library/textures/roughcanvas.bmp b/stuff/projects/library/textures/roughcanvas.bmp new file mode 100644 index 0000000..28d466b Binary files /dev/null and b/stuff/projects/library/textures/roughcanvas.bmp differ diff --git a/stuff/projects/library/textures/roughparquet.bmp b/stuff/projects/library/textures/roughparquet.bmp new file mode 100644 index 0000000..179b57e Binary files /dev/null and b/stuff/projects/library/textures/roughparquet.bmp differ diff --git a/stuff/projects/library/textures/sea.bmp b/stuff/projects/library/textures/sea.bmp new file mode 100644 index 0000000..cbeabbb Binary files /dev/null and b/stuff/projects/library/textures/sea.bmp differ diff --git a/stuff/projects/library/textures/sil.bmp b/stuff/projects/library/textures/sil.bmp new file mode 100644 index 0000000..0983116 Binary files /dev/null and b/stuff/projects/library/textures/sil.bmp differ diff --git a/stuff/projects/library/textures/silver.bmp b/stuff/projects/library/textures/silver.bmp new file mode 100644 index 0000000..9639139 Binary files /dev/null and b/stuff/projects/library/textures/silver.bmp differ diff --git a/stuff/projects/library/textures/snakeskin.bmp b/stuff/projects/library/textures/snakeskin.bmp new file mode 100644 index 0000000..edb5dfa Binary files /dev/null and b/stuff/projects/library/textures/snakeskin.bmp differ diff --git a/stuff/projects/library/textures/snakeskinred.bmp b/stuff/projects/library/textures/snakeskinred.bmp new file mode 100644 index 0000000..d2cc8f6 Binary files /dev/null and b/stuff/projects/library/textures/snakeskinred.bmp differ diff --git a/stuff/projects/library/textures/snow.bmp b/stuff/projects/library/textures/snow.bmp new file mode 100644 index 0000000..0602c5f Binary files /dev/null and b/stuff/projects/library/textures/snow.bmp differ diff --git a/stuff/projects/library/textures/steelplates.bmp b/stuff/projects/library/textures/steelplates.bmp new file mode 100644 index 0000000..e33075b Binary files /dev/null and b/stuff/projects/library/textures/steelplates.bmp differ diff --git a/stuff/projects/library/textures/stucco.bmp b/stuff/projects/library/textures/stucco.bmp new file mode 100644 index 0000000..03570ee Binary files /dev/null and b/stuff/projects/library/textures/stucco.bmp differ diff --git a/stuff/projects/library/textures/wetpebbles.bmp b/stuff/projects/library/textures/wetpebbles.bmp new file mode 100644 index 0000000..b0fb451 Binary files /dev/null and b/stuff/projects/library/textures/wetpebbles.bmp differ diff --git a/stuff/projects/library/textures/woodgrain.bmp b/stuff/projects/library/textures/woodgrain.bmp new file mode 100644 index 0000000..d0725b6 Binary files /dev/null and b/stuff/projects/library/textures/woodgrain.bmp differ diff --git a/stuff/projects/library/textures/woodplanks.bmp b/stuff/projects/library/textures/woodplanks.bmp new file mode 100644 index 0000000..02c9404 Binary files /dev/null and b/stuff/projects/library/textures/woodplanks.bmp differ diff --git a/stuff/projects/library/textures/wornleather.bmp b/stuff/projects/library/textures/wornleather.bmp new file mode 100644 index 0000000..044f5c0 Binary files /dev/null and b/stuff/projects/library/textures/wornleather.bmp differ diff --git a/stuff/projects/library/vector brushes/branch.pli b/stuff/projects/library/vector brushes/branch.pli new file mode 100644 index 0000000..704074d Binary files /dev/null and b/stuff/projects/library/vector brushes/branch.pli differ diff --git a/stuff/projects/library/vector brushes/buttonhole.pli b/stuff/projects/library/vector brushes/buttonhole.pli new file mode 100644 index 0000000..2ac0376 Binary files /dev/null and b/stuff/projects/library/vector brushes/buttonhole.pli differ diff --git a/stuff/projects/library/vector brushes/circle.pli b/stuff/projects/library/vector brushes/circle.pli new file mode 100644 index 0000000..9d71c31 Binary files /dev/null and b/stuff/projects/library/vector brushes/circle.pli differ diff --git a/stuff/projects/library/vector brushes/greek_frieze.pli b/stuff/projects/library/vector brushes/greek_frieze.pli new file mode 100644 index 0000000..12d1c5b Binary files /dev/null and b/stuff/projects/library/vector brushes/greek_frieze.pli differ diff --git a/stuff/projects/library/vector brushes/intertwined_waves.pli b/stuff/projects/library/vector brushes/intertwined_waves.pli new file mode 100644 index 0000000..e367530 Binary files /dev/null and b/stuff/projects/library/vector brushes/intertwined_waves.pli differ diff --git a/stuff/projects/library/vector brushes/large_brush1.pli b/stuff/projects/library/vector brushes/large_brush1.pli new file mode 100644 index 0000000..76bfd6a Binary files /dev/null and b/stuff/projects/library/vector brushes/large_brush1.pli differ diff --git a/stuff/projects/library/vector brushes/large_brush2.pli b/stuff/projects/library/vector brushes/large_brush2.pli new file mode 100644 index 0000000..e47a113 Binary files /dev/null and b/stuff/projects/library/vector brushes/large_brush2.pli differ diff --git a/stuff/projects/library/vector brushes/large_brush3.pli b/stuff/projects/library/vector brushes/large_brush3.pli new file mode 100644 index 0000000..6af1271 Binary files /dev/null and b/stuff/projects/library/vector brushes/large_brush3.pli differ diff --git a/stuff/projects/library/vector brushes/large_brush4.pli b/stuff/projects/library/vector brushes/large_brush4.pli new file mode 100644 index 0000000..f707a97 Binary files /dev/null and b/stuff/projects/library/vector brushes/large_brush4.pli differ diff --git a/stuff/projects/library/vector brushes/logo.pli b/stuff/projects/library/vector brushes/logo.pli new file mode 100644 index 0000000..5a8c31f Binary files /dev/null and b/stuff/projects/library/vector brushes/logo.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush1.pli b/stuff/projects/library/vector brushes/medium_brush1.pli new file mode 100644 index 0000000..f825741 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush1.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush2.pli b/stuff/projects/library/vector brushes/medium_brush2.pli new file mode 100644 index 0000000..46d1575 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush2.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush3.pli b/stuff/projects/library/vector brushes/medium_brush3.pli new file mode 100644 index 0000000..46d1575 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush3.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush4.pli b/stuff/projects/library/vector brushes/medium_brush4.pli new file mode 100644 index 0000000..24ff217 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush4.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush5.pli b/stuff/projects/library/vector brushes/medium_brush5.pli new file mode 100644 index 0000000..9182643 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush5.pli differ diff --git a/stuff/projects/library/vector brushes/medium_brush6.pli b/stuff/projects/library/vector brushes/medium_brush6.pli new file mode 100644 index 0000000..6e72cc4 Binary files /dev/null and b/stuff/projects/library/vector brushes/medium_brush6.pli differ diff --git a/stuff/projects/library/vector brushes/scratch.pli b/stuff/projects/library/vector brushes/scratch.pli new file mode 100644 index 0000000..d1ad817 Binary files /dev/null and b/stuff/projects/library/vector brushes/scratch.pli differ diff --git a/stuff/projects/library/vector brushes/scribble_chalk.pli b/stuff/projects/library/vector brushes/scribble_chalk.pli new file mode 100644 index 0000000..6050ce8 Binary files /dev/null and b/stuff/projects/library/vector brushes/scribble_chalk.pli differ diff --git a/stuff/projects/library/vector brushes/simple_lace.pli b/stuff/projects/library/vector brushes/simple_lace.pli new file mode 100644 index 0000000..de035d6 Binary files /dev/null and b/stuff/projects/library/vector brushes/simple_lace.pli differ diff --git a/stuff/projects/library/vector brushes/simple_trail.pli b/stuff/projects/library/vector brushes/simple_trail.pli new file mode 100644 index 0000000..c63db20 Binary files /dev/null and b/stuff/projects/library/vector brushes/simple_trail.pli differ diff --git a/stuff/projects/library/vector brushes/simple_wave.pli b/stuff/projects/library/vector brushes/simple_wave.pli new file mode 100644 index 0000000..896c6a7 Binary files /dev/null and b/stuff/projects/library/vector brushes/simple_wave.pli differ diff --git a/stuff/projects/library/vector brushes/small_brush1.pli b/stuff/projects/library/vector brushes/small_brush1.pli new file mode 100644 index 0000000..eaed98a Binary files /dev/null and b/stuff/projects/library/vector brushes/small_brush1.pli differ diff --git a/stuff/projects/library/vector brushes/small_brush2.pli b/stuff/projects/library/vector brushes/small_brush2.pli new file mode 100644 index 0000000..7e0291c Binary files /dev/null and b/stuff/projects/library/vector brushes/small_brush2.pli differ diff --git a/stuff/projects/library/vector brushes/small_brush3.pli b/stuff/projects/library/vector brushes/small_brush3.pli new file mode 100644 index 0000000..d008cc0 Binary files /dev/null and b/stuff/projects/library/vector brushes/small_brush3.pli differ diff --git a/stuff/projects/library/vector brushes/small_brush4.pli b/stuff/projects/library/vector brushes/small_brush4.pli new file mode 100644 index 0000000..c45be45 Binary files /dev/null and b/stuff/projects/library/vector brushes/small_brush4.pli differ diff --git a/stuff/projects/library/vector brushes/spiked_chain.pli b/stuff/projects/library/vector brushes/spiked_chain.pli new file mode 100644 index 0000000..7664707 Binary files /dev/null and b/stuff/projects/library/vector brushes/spiked_chain.pli differ diff --git a/stuff/projects/library/vector brushes/splashes.pli b/stuff/projects/library/vector brushes/splashes.pli new file mode 100644 index 0000000..73edeea Binary files /dev/null and b/stuff/projects/library/vector brushes/splashes.pli differ diff --git a/stuff/projects/library/vector brushes/stencil_flame.pli b/stuff/projects/library/vector brushes/stencil_flame.pli new file mode 100644 index 0000000..605380c Binary files /dev/null and b/stuff/projects/library/vector brushes/stencil_flame.pli differ diff --git a/stuff/projects/library/vector brushes/strange.pli b/stuff/projects/library/vector brushes/strange.pli new file mode 100644 index 0000000..f6ef766 Binary files /dev/null and b/stuff/projects/library/vector brushes/strange.pli differ diff --git a/stuff/projects/library/vector brushes/teardrop_flowers1.pli b/stuff/projects/library/vector brushes/teardrop_flowers1.pli new file mode 100644 index 0000000..d956f6f Binary files /dev/null and b/stuff/projects/library/vector brushes/teardrop_flowers1.pli differ diff --git a/stuff/projects/library/vector brushes/teardrop_flowers2.pli b/stuff/projects/library/vector brushes/teardrop_flowers2.pli new file mode 100644 index 0000000..4ed2d09 Binary files /dev/null and b/stuff/projects/library/vector brushes/teardrop_flowers2.pli differ diff --git a/stuff/projects/library/vector brushes/thickening.pli b/stuff/projects/library/vector brushes/thickening.pli new file mode 100644 index 0000000..3a010b5 Binary files /dev/null and b/stuff/projects/library/vector brushes/thickening.pli differ diff --git a/stuff/projects/library/vector brushes/trail_shape1.pli b/stuff/projects/library/vector brushes/trail_shape1.pli new file mode 100644 index 0000000..cfdce2e Binary files /dev/null and b/stuff/projects/library/vector brushes/trail_shape1.pli differ diff --git a/stuff/projects/library/vector brushes/trail_shape2.pli b/stuff/projects/library/vector brushes/trail_shape2.pli new file mode 100644 index 0000000..04b1f9b Binary files /dev/null and b/stuff/projects/library/vector brushes/trail_shape2.pli differ diff --git a/stuff/projects/reslist.txt b/stuff/projects/reslist.txt new file mode 100644 index 0000000..5ddea89 --- /dev/null +++ b/stuff/projects/reslist.txt @@ -0,0 +1,91 @@ +PAL analog, 768x576, 4/3 + +PAL digital, 720x576, 4/3 + +PAL 16/9, 1024x576, 16/9 + +NTSC digital, 720x486, 4/3 + +NTSC analog, 648x486, 4/3 + +NTSC 16/9, 864x486, 16/9 + +HD 1080, 1920x1080, 16/9 + +HD 720, 1280x720, 16/9 + +HD 480, 853x480, 16/9 + +Academy Full Frame1 1K, 914x666, 1.37 + +Academy Full Frame2 1K, 1024x746, 1.37 + +Academy Full Frame1 2K, 1828x1332, 1.37 + +Academy Full Frame2 2K, 2048x1494, 1.37 + +Academy Full Frame1 4K, 3656x2664, 1.37 + +Academy Full Frame2 4K, 4096x2988, 1.37 + +Full Camera Aperture 1K, 1024x768, 4/3 + +1K, 1024x768, 4/3 + +Full Camera Aperture 2K, 2048x1536, 4/3 + +2K, 2048x1536, 4/3 + +3K, 3072x2304, 4/3 + +Full Camera Aperture 4k, 4096x3072, 4/3 + +4K, 4096x3072, 4/3 + +Cineon Half Unsqueezed, 3675x1556, 2.36183 + +Cineon Half, 1828x1556, 2.35 + +Cineon Half Squeezed, 1828x1556, 2.36183 + +Cineon Full, 3656x3112, 2.35 + +Cineon Full, 4704x3112, 2.36183 + +Academy Projection1 1K, 914x550, 1.66 + +Academy Projection2 1K, 1024x617, 1.66 + +Academy Projection1 2K, 1828x1102, 1.66 + +Academy Projection2 2K, 2048x1229, 1.66 + +Academy Projection1 4K, 3656x2202, 1.66 + +Academy Projection2 4K, 4096x2468, 1.66 + +Academy Projection1 1K, 914x494, 1.85 + +Academy Projection2 1K, 1024x554, 1.85 + +Academy Projection1 2K, 1828x988, 1.85 + +Academy Projection2 2K, 2048x1107, 1.85 + +Academy Projection1 4K, 3656x1976, 1.85 + +Academy Projection2 4K, 4096x2214, 1.85 + +Anamorphic Pre-squeezed, 1K 914x774, 1.18 + +Anamorphic Pre-squeezed, 2K 1828x1550, 1.18 + +Anamorphic Pre-squeezed, 4K 3656x3098, 1.18 + +Anamorphic Un-squeezed, 1K 914x388, 2.35 + +Anamorphic Un-squeezed, 2K 1828x778, 2.35 + +Anamorphic Un-squeezed, 4K 3656x1556, 2.35 + + diff --git a/stuff/projects/safearea.ini b/stuff/projects/safearea.ini new file mode 100644 index 0000000..ec2118c --- /dev/null +++ b/stuff/projects/safearea.ini @@ -0,0 +1,26 @@ +[SafeArea0] +area\0=80, 80 +area\1=90, 90 +name=PR_safe + +[SafeArea1] +area\0=95.5, 95.5, 0, 255, 0 +name=FR_PR + +[SafeArea2] +area\0=95.5, 95.5, 0, 255, 0 +area\1=85.95, 85.95 +area\2=76.4, 76.4 +name=FR_PR_safe + +[SafeArea3] +area\0=98.53, 93.85, 0, 0, 255 +area\1=94.12, 89.64, 0, 255, 0 +name=150MT_FR_PR + +[SafeArea4] +area\0=98.53, 93.85, 0, 0, 255 +area\1=94.12, 89.64, 0, 255, 0 +area\2=84.7, 80.68 +area\3=75.3, 71.71 +name=150MT_FR_PR_safe diff --git a/stuff/studiopalette/cleanup_default.tpl b/stuff/studiopalette/cleanup_default.tpl new file mode 100644 index 0000000..6dafab7 --- /dev/null +++ b/stuff/studiopalette/cleanup_default.tpl @@ -0,0 +1,785 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + colors + + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 + + + + + 0 1 -1 -1 -1 -1 -1 -1 -1 -1 + + diff --git a/thirdparty/.gitignore b/thirdparty/.gitignore new file mode 100644 index 0000000..3f896cb --- /dev/null +++ b/thirdparty/.gitignore @@ -0,0 +1,3 @@ +* +# preserve this empty directory +!.gitignore diff --git a/toonz/install/SystemVar.ini b/toonz/install/SystemVar.ini new file mode 100644 index 0000000..a768d09 --- /dev/null +++ b/toonz/install/SystemVar.ini @@ -0,0 +1,11 @@ +[General] +TOONZCACHEROOT="/Applications/OpenToonz/OpenToonz_1.0_stuff/cache" +TOONZCONFIG="/Applications/OpenToonz/OpenToonz_1.0_stuff/config" +TOONZFXPRESETS="/Applications/OpenToonz/OpenToonz_1.0_stuff/projects/fxs" +TOONZLIBRARY="/Applications/OpenToonz/OpenToonz_1.0_stuff/projects/library" +TOONZPROFILES="/Applications/OpenToonz/OpenToonz_1.0_stuff/profiles" +OpenToonzPROFILES="/Applications/OpenToonz/OpenToonz_1.0_stuff/profiles" +TOONZPROJECTS="/Applications/OpenToonz/OpenToonz_1.0_stuff/projects" +TOONZROOT="/Applications/OpenToonz/OpenToonz_1.0_stuff" +OPENTOONZROOT="/Applications/OpenToonz/OpenToonz_1.0_stuff" +TOONZSTUDIOPALETTE="/Applications/OpenToonz/OpenToonz_1.0_stuff/projects/studiopalette" diff --git a/toonz/install/configfarmroot.txt b/toonz/install/configfarmroot.txt new file mode 100644 index 0000000..3bf9229 --- /dev/null +++ b/toonz/install/configfarmroot.txt @@ -0,0 +1 @@ +/Applications/Toonz 7.1/Toonz 7.1 stuff/toonzfarm/ diff --git a/toonz/install/copy_plugin.sh b/toonz/install/copy_plugin.sh new file mode 100644 index 0000000..3e493d9 --- /dev/null +++ b/toonz/install/copy_plugin.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +echo ' Create Frameworks folder inside toonz 7.1.app/Contents…' +mkdir $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks + +echo ' Copy tnzcore to Frameworks...': +cp -RH $BINROOT/bin/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy tnzbase to Frameworks...' +cp -RH $BINROOT/bin/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy tnzext to Frameworks...' +cp -RH $BINROOT/bin/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy toonzlib to Frameworks...' +cp -RH $BINROOT/bin/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy image to Frameworks...' +cp -RH $BINROOT/bin/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy sound to Frameworks...' +cp -RH $BINROOT/bin/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy colorfx to Frameworks...' +cp -RH $BINROOT/bin/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy tnzstdfx to Frameworks...' +cp -RH $BINROOT/bin/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy tfarm to Frameworks...' +cp -RH $BINROOT/bin/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy tnztools to Frameworks...' +cp -RH $BINROOT/bin/libtnztools.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks +echo ' Copy toonzqt to Frameworks...' +cp -RH $BINROOT/bin/libtoonzqt.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks + +echo ' Copy composer...' +cp -RH $BINROOT/bin/tcomposer.app/Contents/MacOS/tcomposer $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ +echo ' Copy cleanup...' +cp -RH $BINROOT/bin/tcleanup.app/Contents/MacOS/tcleanup $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ +echo ' Copy farmserver...' +cp -RH $BINROOT/bin/tfarmserver.app/Contents/MacOS/tfarmserver $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ +echo ' Copy farmcontroller...' +cp -RH $BINROOT/bin/tfarmcontroller.app/Contents/MacOS/tfarmcontroller $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ +echo ' Copy converter...' +cp -RH $BINROOT/bin/tconverter.app/Contents/MacOS/tconverter $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ + +echo ' Copy t32bitsrv...' +lipo -remove x86_64 $BINROOT/bin/t32bitsrv.app/Contents/MacOS/t32bitsrv -output $BINROOT/bin/t32bitsrv.app/Contents/MacOS/t32bitsrv +cp -RH $BINROOT/bin/t32bitsrv.app/Contents/MacOS/t32bitsrv $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/ + +echo ' Copy QTCore 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4 +cp /Library/Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/ +echo ' Copy QTGui 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4 +cp /Library/Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/ +echo ' Copy QTOpenGL 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4 +cp /Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/ +echo ' Copy QtXml 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtXml.framework/Versions/4 +cp /Library/Frameworks/QtXml.framework/Versions/4/QtXml $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtXml.framework/Versions/4/ +echo ' Copy QtSvg 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4 +cp /Library/Frameworks/QtSvg.framework/Versions/4/QtSvg $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4/ +echo ' Copy QtNetwork 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4 +cp /Library/Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/ +echo ' Copy QtScript 4.8 Frameworks...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtScript.framework/Versions/4 +cp /Library/Frameworks/QtScript.framework/Versions/4/QtScript $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtScript.framework/Versions/4/ + +echo ' Copy Qt plugins...' +mkdir -p $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats +cp /Developer/Applications/Qt/plugins/imageformats/libqjpeg.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/ +cp /Developer/Applications/Qt/plugins/imageformats/libqgif.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/ +cp /Developer/Applications/Qt/plugins/imageformats/libqtiff.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/ +cp /Developer/Applications/Qt/plugins/imageformats/libqsvg.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/ + +echo ' Copy GLEW' +cp -RH /depot/sdk/glew/glew-1.9.0/lib/libGLEW.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks + +echo ' Create qt.conf...' +mkdir $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources +echo 'Prefix = .' > $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/qt.conf +echo ' Copy SystemVar...' +cp ./SystemVar.ini $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/ +chmod 777 $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/SystemVar.ini +echo ' Copy configfarmroot...' +cp ./configfarmroot.txt $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/ +chmod 777 $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/configfarmroot.txt +echo ' Copy qt_menu.nib...' +cp -R /Library/Frameworks/QtGui.framework/Versions/Current/Resources/qt_menu.nib $BINROOT/bin/Toonz\ 7.1.app/Contents/Resources/ diff --git a/toonz/install/creatorfinalizeProgram.sh b/toonz/install/creatorfinalizeProgram.sh new file mode 100644 index 0000000..a508a62 --- /dev/null +++ b/toonz/install/creatorfinalizeProgram.sh @@ -0,0 +1,7 @@ +#!/bin/tcsh +cp $SCRIPTROOT/toonzdef.$1 $HOME/toonzdef +source $HOME/.cshrc +source $HOME/.login +./copy_plugin.sh +./installName.sh + diff --git a/toonz/install/finalizeProgram.sh b/toonz/install/finalizeProgram.sh new file mode 100644 index 0000000..9cc272e --- /dev/null +++ b/toonz/install/finalizeProgram.sh @@ -0,0 +1,4 @@ +#!/bin/sh +./copy_plugin.sh +./installName.sh + diff --git a/toonz/install/installName.sh b/toonz/install/installName.sh new file mode 100644 index 0000000..81a94cb --- /dev/null +++ b/toonz/install/installName.sh @@ -0,0 +1,529 @@ + +#!/bin/sh + +# TOONZ LIBRARIES + +# RICORDARSI PRIMA DI COPIARE LE LIBRERIE IN Toonz\ 7.1.app/Contents/Frameworks + +#echo /depot/angelo/toonz/main/binmacosxd + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# tnzcore dipende da QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib + + +# tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# tnzbase dipende da tnzcore QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib + + +# toonzlib + +install_name_tool -id @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# toonzlib dipende da tnzbase tnzcore tnzext QtGui QtCore QtOpenGl QtNetwork e QtScript + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtnzext.1.dylib @executable_path/../Frameworks/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtScript.framework/Versions/4/QtScript @executable_path/../Frameworks/QtScript.framework/Versions/4/QtScript $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib + + +# image + +install_name_tool -id @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libimage.1.dylib @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# image dipende da tnzbase tnzcore QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib + + +# sound + +install_name_tool -id @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change libsound.1.dylib @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# sound dipende da tnzcore QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib + + +# tnzext + +install_name_tool -id @executable_path/../Frameworks/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change libtnzext.1.dylib @executable_path/../Frameworks/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# tnzext dipende da tnzcore tnzbase QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzext.1.dylib + + +# tnzstdfx + +install_name_tool -id @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change libtnzstdfx.1.dylib @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 +install_name_tool -id @executable_path/../Frameworks/libGLEW.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change /usr/lib/libGLEW.1.9.0.dylib @executable_path/../Frameworks/libGLEW.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib + +# image dipende da tnzbase tnzcore toonzlib QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib + + +# colorfx + +install_name_tool -id @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change libcolorfx.1.dylib @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# colorfx dipende da tnzbase tnzcore toonzlib QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib + + +# tfarm + +install_name_tool -id @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtfarm.1.dylib @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# tfarm dipende da tnzbase tnzcore toonzlib QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib + + +# toonzqt + +install_name_tool -id @executable_path/../Frameworks/libtoonzqt.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change libtoonzqt.1.dylib @executable_path/../Frameworks/libtoonzqt.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# toonzqt dipende da tnzbase tnzcore toonzlib tnzext sound QtGui QtCore QtOpenGl e QtNetwork + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change libtnzext.1.dylib @executable_path/../Frameworks/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change libsound.1.dylib @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzqt.1.dylib + + +# tnztools + +install_name_tool -id @executable_path/../Frameworks/libtnztools.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change libtnztools.1.dylib @executable_path/../Frameworks/libtnztools.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +# tnztools dipende da tnzbase tnzcore toonzlib toonzqt tnzext QtGui QtCore QtOpenGL + +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change libtoonzqt.1.dylib @executable_path/../Frameworks/libtoonzqt.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change libtnzext.1.dylib @executable_path/../Frameworks/libtnzext.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnztools.1.dylib + + + + + + +#QT LIBRARIES + +# RICORDARSI PRIMA DI COPIARE I FRAMEWORK QT (AD ESEMPIO PER COPIARE QtCore) +# cp -R /Library/Frameworks/QtCore.framework Toonz\ 7.1.app/Contents/Frameworks + +# $BINROOT/bin/deployqt $BINROOT/bin/Toonz\ 7.1.app + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +install_name_tool -id @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtXml.framework/Versions/4/QtXml +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtXml.framework/Versions/4/QtXml @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +install_name_tool -id @executable_path/../Frameworks/QtSvg.framework/Versions/4/QtSvg $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4/QtSvg +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtSvg.framework/Versions/4/QtSvg @executable_path/../Frameworks/QtSvg.framework/Versions/4/QtSvg $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + + +install_name_tool -id @executable_path/../Frameworks/QtScript.framework/Versions/4/QtScript $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtScript.framework/Versions/4/QtScript +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtScript.framework/Versions/4/QtScript @executable_path/../Frameworks/QtScript.framework/Versions/4/QtScript $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + + +# QtGui dipende da QtCore... gli aggiorno le path (internamente) + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui + +# QtXml dipende da QTCore + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtXml.framework/Versions/4/QtXml + +# QtOpenGL dipende da QtGui e da QtCore ... gli aggiorno le path (internamente) + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL + +# QtNetwork dipende da QTCore + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork + +# QtSvg dipende da QTCore QTGui + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4/QtSvg + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4/QtSvg + +install_name_tool -change QtXml.framework/Versions/4/QtXml @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtSvg.framework/Versions/4/QtSvg + +# QtScript dipende da QTCore + +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtScript.framework/Versions/4/QtScript + + +# PLUGINS QT (jpg) dipende da QtGui e QtCore + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqjpeg.dylib +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqjpeg.dylib + +# PLUGINS QT (GIF) dipende da QtGui e QtCore + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqgif.dylib +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqgif.dylib + +# PLUGINS QT (TIFF) dipende da QtGui e QtCore + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqtiff.dylib +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqtiff.dylib + +# PLUGINS QT (svg) dipende da QtGui QtCore QtSvg e QtXml + +install_name_tool -change QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqsvg.dylib +install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqsvg.dylib +install_name_tool -change QtSvg.framework/Versions/4/QtSvg @executable_path/../Frameworks/QtSvg.framework/Versions/4/QtSvg $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqsvg.dylib +install_name_tool -change QtXml.framework/Versions/4/QtXml @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/imageformats/libqsvg.dylib + + +# GLUT forse non serve + +#install_name_tool -id @executable_path/../Frameworks/libglut.3.dylib Toonz\ 7.1.app/Contents/Frameworks/libglut.3.dylib +#install_name_tool -change /sw/lib/libglut.3.dylib @executable_path/../Frameworks/libglut.3.dylib Toonz\ 7.1.app/Contents/MacOS/Toonz\ 7.1 + + + +# modifico le liblrerie in tcomposer + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + + +# tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# toonzlib + +install_name_tool -id @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# sound + +install_name_tool -id @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change libsound.1.dylib @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# tnzstdfx + +install_name_tool -id @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change libtnzstdfx.1.dylib @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# image + +install_name_tool -id @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libimage.1.dylib @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# colorfx + +install_name_tool -id @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change libcolorfx.1.dylib @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# tfarm + +install_name_tool -id @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtfarm.1.dylib @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcomposer + +# modifico le liblrerie in t32bitsrv + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/t32bitsrv + + +# image + +install_name_tool -id @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libimage.1.dylib @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/t32bitsrv + + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/t32bitsrv + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/t32bitsrv + + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/t32bitsrv + + + + +# modifico le liblrerie in tcleanup + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + + +# tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# toonzlib + +install_name_tool -id @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# sound + +install_name_tool -id @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libsound.1.dylib +install_name_tool -change libsound.1.dylib @executable_path/../Frameworks/libsound.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# tnzstdfx + +install_name_tool -id @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzstdfx.1.dylib +install_name_tool -change libtnzstdfx.1.dylib @executable_path/../Frameworks/libtnzstdfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# image + +install_name_tool -id @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libimage.1.dylib @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# colorfx + +install_name_tool -id @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libcolorfx.1.dylib +install_name_tool -change libcolorfx.1.dylib @executable_path/../Frameworks/libcolorfx.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# tfarm + +install_name_tool -id @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtfarm.1.dylib @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tcleanup + + + +# modifico le liblrerie in tfarmserver + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + +#tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + + +# tfarm + +install_name_tool -id @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtfarm.1.dylib @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmserver + + + +# modifico le liblrerie in tfarmcontroller + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +#tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +# tfarm + +install_name_tool -id @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtfarm.1.dylib +install_name_tool -change libtfarm.1.dylib @executable_path/../Frameworks/libtfarm.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tfarmcontroller + + + +# modifico le liblrerie in tconverter + +#tnzcore + +install_name_tool -id @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzcore.1.dylib +install_name_tool -change libtnzcore.1.dylib @executable_path/../Frameworks/libtnzcore.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + + +# tnzbase + +install_name_tool -id @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtnzbase.1.dylib +install_name_tool -change libtnzbase.1.dylib @executable_path/../Frameworks/libtnzbase.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +# toonzlib + +install_name_tool -id @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libtoonzlib.1.dylib +install_name_tool -change libtoonzlib.1.dylib @executable_path/../Frameworks/libtoonzlib.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +# image + +install_name_tool -id @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/libimage.1.dylib +install_name_tool -change libimage.1.dylib @executable_path/../Frameworks/libimage.1.dylib $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +# Qt + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +install_name_tool -id @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtOpenGL.framework/Versions/4/QtOpenGL @executable_path/../Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork +install_name_tool -change /usr/local/Trolltech/Qt-4.8.0/lib/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork $BINROOT/bin/Toonz\ 7.1.app/Contents/MacOS/tconverter + + + + + diff --git a/toonz/sources/CMakeLists.txt b/toonz/sources/CMakeLists.txt new file mode 100644 index 0000000..9596322 --- /dev/null +++ b/toonz/sources/CMakeLists.txt @@ -0,0 +1,198 @@ +cmake_minimum_required(VERSION 2.8.11) +project(OpenToonz) + +get_filename_component(SDKROOT ../../thirdparty/ ABSOLUTE) +message("SDK Root:" ${SDKROOT}) + +if (WIN32) + message("Windows System") + set(QT_PATH "C:/Qt/Qt5.5.1/5.5/msvc2013_64" CACHE PATH "Qt instlattion directory") + if(NOT EXISTS ${QT_PATH}) + message("Specify QT_PATH properly") + return() + endif() + set(QT_LIB_PATH ${QT_PATH}) + set(CMAKE_PREFIX_PATH "${QT_PATH}/lib/cmake/") +elseif (APPLE) + message("Apple System") + if (NOT PLATFORM) + set(PLATFORM 64) + endif() + message("PLATFORM:" ${PLATFORM}) + if (PLATFORM EQUAL 64) + set(QT_PATH "~/Qt/5.5/clang_64/lib/") + set(QT_LIB_PATH ${QT_PATH}) + set(CMAKE_PREFIX_PATH ${QT_PATH}cmake/) + message("CMAKE_PREFIX_PATH:" ${CMAKE_PREFIX_PATH}) + add_definitions(-DMACOSX -Di386) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -std=c++11 -stdlib=libc++ -fno-implicit-templates") + elseif(PLATFORM EQUAL 32) + set(QT_PATH "~/Qt/5.5/clang_32/lib/") + set(QT_LIB_PATH ${QT_PATH}) + set(CMAKE_PREFIX_PATH ${QT_PATH}cmake/) + message("CMAKE_PREFIX_PATH:" ${CMAKE_PREFIX_PATH}) + add_definitions(-DMACOSX -Di386) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -std=c++11 -stdlib=libc++ -fno-implicit-templates -D HAS_QUICKDRAW") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") + set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -m32") + else() + message(FATAL_ERROR "Invalid PLATFORM:" ${PLATFORM} ". 'PLATFORM' must be 32 or 64.") + endif() +else () + message("Unknown System") +endif () + +add_definitions(-DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_SHARED) + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_INSTALL_RPATH ${QT_LIB_PATH}) +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + +# Find the QtWidgets library +find_package(Qt5 REQUIRED + Core + Gui + Network + OpenGL + Svg + Xml + Script + Widgets + PrintSupport + LinguistTools) + +include_directories(include ${SDKROOT}/boost/boost_1_55_0/) + +if(WIN32) + include_directories( + ${SDKROOT}/glut/3.7.6/include + ${SDKROOT}/zlib/zlib-1.2.7 + ${SDKROOT}/LibJPEG/jpeg-9) + add_definitions(-DGLUT_NO_LIB_PRAGMA) +endif() + +get_target_property(QtCore_location Qt5::Core LOCATION) +get_target_property(QtWidget_location Qt5::Widgets LOCATION) +message("Qt Core Location:" ${QtCore_location}) + +macro(_find_toonz_library OUT_LIB_LIST IN_LIB_LIST) + set(OUTLIST "") + foreach(arg ${IN_LIB_LIST}) + unset(COREPATH CACHE) + # find しても cmake が走るときにはできていないので完全なパス名を生成して返すだけ + if(WIN32) + set(LIBPATH ${arg}) + else() + set(LIBPATH ${CMAKE_CURRENT_BINARY_DIR}/../${arg}/lib${arg}${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + set(OUTLIST ${OUTLIST} ${LIBPATH}) + endforeach() + set(${OUT_LIB_LIST} ${OUTLIST}) +endmacro() + +set(TNZSTDFX_Location) +set(TFARM_Location) + +if(WIN32) + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PLATFORM 32) + set(PLATFORM2) + else() + set(PLATFORM 64) + set(PLATFORM2 _64) + endif() + + set(GLUT_LIB ${SDKROOT}/glut/3.7.6/lib/glut${PLATFORM}.lib) + set(GL_LIB opengl32.lib) + set(Z_LIB + optimized ${SDKROOT}/zlib/zlib-1.2.7/lib/zlib-1.2.7${PLATFORM2}.lib + debug ${SDKROOT}/zlib/zlib-1.2.7/lib/zlib-1.2.7${PLATFORM2}d.lib) + set(JPEG_LIB ${SDKROOT}/LibJPEG/jpeg-9/lib/LibJPEG-9${PLATFORM2}.lib) + set(TIFF_LIB + optimized ${SDKROOT}/LibTIFF/tiff-4.0.3/lib/LibTIFF-4.0.3${PLATFORM2}.lib + debug ${SDKROOT}/LibTIFF/tiff-4.0.3/lib/LibTIFF-4.0.3${PLATFORM2}d.lib) + set(GLEW_LIB ${SDKROOT}/glew/glew-1.9.0/lib/glew${PLATFORM}.lib) + set(LZ4_LIB ${SDKROOT}/Lz4/Lz4_131/lz4_${PLATFORM}.lib) + set(SUPERLU_LIB ${SDKROOT}/superlu/SuperLU_${PLATFORM}.lib) + set(OPENBLAS_LIB ${SDKROOT}/openblas/libopenblas_${PLATFORM}.lib) + set(USB_LIB) # unused + if (PLATFORM EQUAL 32) + set(QT_LIB ${SDKROOT}/quicktime/QT73SDK/Libraries/QTMLClient.lib) + endif() +else() + find_library(GLUT_LIB GLUT) + find_library(GL_LIB OpenGL) + find_library(Z_LIB z) + set(JPEG_LIB ${SDKROOT}/LibJPEG/jpeg-9/libjpeg.a) + set(TIFF_LIB ${SDKROOT}/LibTIFF/tiff-4.0.3/lib/libtiff.a) + set(GLEW_LIB ${SDKROOT}/glew/glew-1.9.0/lib/libGLEW.dylib) + set(LZ4_LIB ${SDKROOT}/Lz4/Lz4_131/lib/liblz4.a) + set(SUPERLU_LIB ${SDKROOT}/superlu/libsuperlu_4.1.a) + set(OPENBLAS_LIB) # unused? + set(USB_LIB ${SDKROOT}/libusb/libusb-1.0.9/libusb/.libs/libusb-1.0.a) + if (PLATFORM EQUAL 32) + find_library(QT_LIB QuickTime) + endif() +endif() + +if(WIN32 AND PLATFORM EQUAL 64) + add_definitions(-Dx64) +endif() + +if(WIN32) + # place Toonz.exe and dlls in the same directory + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") +endif() + +# generate Qt translations and messages +set(LANGUAGES japanese italian french) + +function(add_translation module) + set(translation) + foreach(lang ${LANGUAGES}) + set(name "${CMAKE_SOURCE_DIR}/translations/${lang}/${module}.ts") + list(APPEND translation ${name}) + set_source_files_properties(${name} PROPERTIES + OUTPUT_LOCATION "${CMAKE_BINARY_DIR}/loc/${lang}") + endforeach() + + qt5_create_translation(message ${translation} ${ARGN}) + + add_custom_target("translation_${module}" DEPENDS ${message}) + set_target_properties("translation_${module}" PROPERTIES + EXCLUDE_FROM_DEFAULT_BUILD TRUE) +endfunction() + +add_subdirectory(tnzcore) +add_subdirectory(tnzbase) +add_subdirectory(tnzext) +add_subdirectory(toonzlib) +add_subdirectory(toonzfarm/tfarm) +add_subdirectory(stdfx) +add_subdirectory(sound) +add_subdirectory(colorfx) +add_subdirectory(image) +add_subdirectory(toonzqt) +add_subdirectory(tnztools) +add_subdirectory(toonz) + +add_subdirectory(${SDKROOT}/lzo/driver lzodriver) + +add_subdirectory(tcleanupper) +add_subdirectory(tcomposer) +add_subdirectory(tconverter) +add_subdirectory(toonzfarm) +if(PLATFORM EQUAL 32) + add_subdirectory(t32bitsrv) +endif() + +if (APPLE) + add_custom_command(TARGET executable + POST_BUILD COMMAND + ${CMAKE_INSTALL_NAME_TOOL} -add_rpath ${CMAKE_INSTALL_RPATH} + $) +endif (APPLE) diff --git a/toonz/sources/colorfx/CMakeLists.txt b/toonz/sources/colorfx/CMakeLists.txt new file mode 100644 index 0000000..037eb6f --- /dev/null +++ b/toonz/sources/colorfx/CMakeLists.txt @@ -0,0 +1,33 @@ +set(HEADERS + ../include/colorfx.h + colorfxutils.h + rasterstyles.h + regionstyles.h + strokestyles.h + zigzagstyles.h) + +set(SOURCES + colorfx.cpp + colorfxutils.cpp + rasterstyles.cpp + regionstyles.cpp + strokestyles.cpp + zigzagstyles.cpp) + +add_translation(colorfx ${HEADERS} ${SOURCES}) + +add_library(colorfx SHARED ${HEADERS} ${SOURCES}) +add_definitions(-D _USRDLL -DCOLORFX_EXPORTS -DUSE_MESA) +if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-install_name,@rpath/libcolorfx.dylib") +endif (APPLE) + +message("subdir: colorfx") +message("type:" ${CMAKE_SHARED_LIBRARY_SUFFIX}) +message("Bin: " ${CMAKE_CURRENT_BINARY_DIR}) + +_find_toonz_library(EXTRA_LIBS "tnzcore;tnzbase") + +message("FIND_FILE:" ${EXTRA_LIBS}) + +target_link_libraries(colorfx Qt5::Core ${GL_LIB} ${EXTRA_LIBS}) diff --git a/toonz/sources/colorfx/colorfx.cpp b/toonz/sources/colorfx/colorfx.cpp new file mode 100644 index 0000000..b91763d --- /dev/null +++ b/toonz/sources/colorfx/colorfx.cpp @@ -0,0 +1,105 @@ + + +// #include "tpluginmanager.h" +#include "strokestyles.h" +#include "regionstyles.h" +#include "rasterstyles.h" +#include "colorfx.h" + +// static TPluginInfo info("ColorFxPlugin"); + +//------------------------------------------------------------------- + +namespace +{ + +//------------------------------------------------------------------- + +void add(TColorStyle *s) +{ + TColorStyle::declare(s); +} + +//------------------------------------------------------------------- + +} // namespace + +//------------------------------------------------------------------- + +void initColorFx() +{ + //add(new TFriezeStrokeStyle); + + add(new TRopeStrokeStyle); + + add(new TChainStrokeStyle); + add(new TFurStrokeStyle); + //add(new TChalkStrokeStyle); + //add(new TBumpStrokeStyle); + //add(new TBlendStrokeStyle); + + add(new TDottedLineStrokeStyle); + + add(new TBraidStrokeStyle); + add(new TSketchStrokeStyle); + add(new TBubbleStrokeStyle); + add(new TGraphicPenStrokeStyle); + add(new TCrystallizeStrokeStyle); + add(new TSprayStrokeStyle); + add(new TTissueStrokeStyle); + //add(new TMultiLineStrokeStyle); + add(new TBiColorStrokeStyle); + add(new TNormal2StrokeStyle); + //add(new TNormalStrokeStyle); + //add(new TLongBlendStrokeStyle); + add(new TChalkStrokeStyle2); + //add(new TDualColorStrokeStyle); + + add(new TBlendStrokeStyle2); + add(new TTwirlStrokeStyle); + + add(new TMultiLineStrokeStyle2); + add(new TZigzagStrokeStyle); //non funziona su linux, rivedere + add(new TSinStrokeStyle); + + add(new TFriezeStrokeStyle2); + add(new TDualColorStrokeStyle2()); // non funziona (massimo) su linux, rivedere + add(new TLongBlendStrokeStyle2()); + + add(new TMatrioskaStrokeStyle()); + +#ifdef _DEBUG + add(new OutlineViewerStyle()); +#endif + + add(new MovingSolidColor(TPixel32::Blue, TPointD(10, 10))); //ok + //add(new MovingTexture(readTexture("chessboard.bmp"),TTextureStyle::NONE,TPointD(10,10) )); + + add(new ShadowStyle(TPixel32::White, TPixel32::Black)); + add(new ShadowStyle2(TPixel32::Yellow, TPixel32::Magenta)); + + add(new TRubberFillStyle(TPixel32(255, 0, 255, 127), 25.0)); + add(new TPointShadowFillStyle(TPixel32(255, 255, 200), TPixel32(215, 0, 0))); + + add(new TDottedFillStyle(TPixel32::Green)); + + add(new TCheckedFillStyle(TPixel32(255, 0, 0, 128))); + add(new ArtisticSolidColor(TPixel32(0, 130, 255), TPointD(10, 10), 100)); + + add(new TChalkFillStyle(TPixel32::White, TPixel32::Black)); //non funziona + add(new TChessFillStyle(TPixel32::Red)); + + add(new TSawToothStrokeStyle); + add(new TStripeFillStyle(TPixel32::Green)); + + add(new TLinGradFillStyle(TPixel32::Green)); + add(new TRadGradFillStyle(TPixel32::Green)); + add(new TCircleStripeFillStyle(TPixel32::Green)); + add(new TMosaicFillStyle(TPixel32::Red)); + add(new TPatchFillStyle(TPixel32::Blue)); +#ifndef BRAVO + add(new TAirbrushRasterStyle(TPixel32::Black, 10)); + add(new TBlendRasterStyle(TPixel32::Black, 10)); + add(new TNoColorRasterStyle()); +#endif +} diff --git a/toonz/sources/colorfx/colorfxutils.cpp b/toonz/sources/colorfx/colorfxutils.cpp new file mode 100644 index 0000000..5ed81c4 --- /dev/null +++ b/toonz/sources/colorfx/colorfxutils.cpp @@ -0,0 +1,504 @@ + + +//#include "tutil.h" +//#include "tgl.h" +#include "colorfxutils.h" +#include "drawutil.h" +#include "tregion.h" +#include "tflash.h" + +RubberDeform::RubberDeform() : m_pPolyOri(0), m_polyLoc() +{ +} + +RubberDeform::RubberDeform(vector *pPolyOri, const double rf) : m_pPolyOri(pPolyOri), + m_polyLoc() +{ + copyOri2Loc(); + TRectD bbox; + getBBox(bbox); + double d = tdistance(TPointD(bbox.x0, bbox.y0), TPointD(bbox.x1, bbox.y1)); + d = d / 20; + refinePoly(d); +} + +RubberDeform::~RubberDeform() +{ +} + +void RubberDeform::deformStep() +{ + vector tmpv; + vector::iterator itb = m_polyLoc.begin(); + vector::iterator ite = m_polyLoc.end(); + for (vector::iterator it = itb; it != ite; ++it) { + vector::iterator it1 = it == (ite - 1) ? itb : it + 1; + double q = 0.5; + double qq = 1.0 - q; + tmpv.push_back(T3DPointD(qq * it->x + q * it1->x, qq * it->y + q * it1->y, qq * it->z + q * it1->z)); + } + m_polyLoc = tmpv; +} + +void RubberDeform::deform(const double n) +{ + if (n <= 0 || n >= 100) + return; + double q = (double)n / 100.0; + TRectD bbox; + getBBox(bbox); + double d0 = ((bbox.y1 - bbox.y0) * 0.5 + (bbox.x1 - bbox.x0) * 0.5) * 0.5; + double d = d0; + while ((d / d0) > q) { + deformStep(); + getBBox(bbox); + d = ((bbox.y1 - bbox.y0) * 0.5 + (bbox.x1 - bbox.x0) * 0.5) * 0.5; + } + copyLoc2Ori(); +} + +double RubberDeform::avgLength() +{ + if (m_polyLoc.size() <= 0) + return 0.0; + + double avgD = 0.0; + vector::iterator itb = m_polyLoc.begin(); + vector::iterator ite = m_polyLoc.end(); + for (vector::iterator it = itb; it != ite; ++it) { + vector::iterator it1 = it == (ite - 1) ? itb : it + 1; + avgD += tdistance(*it, *it1); + } + return avgD / (double)m_polyLoc.size(); +} + +void RubberDeform::getBBox(TRectD &bbox) +{ + if (m_polyLoc.size() <= 0) { + bbox.x0 = bbox.y0 = 0; + bbox.x1 = bbox.y1 = -1; + return; + } + bbox.x0 = bbox.x1 = m_polyLoc[0].x; + bbox.y0 = bbox.y1 = m_polyLoc[0].y; + for (int i = 1; i < (int)m_polyLoc.size(); i++) { + bbox.x0 = tmin(bbox.x0, m_polyLoc[i].x); + bbox.x1 = tmax(bbox.x1, m_polyLoc[i].x); + bbox.y0 = tmin(bbox.y0, m_polyLoc[i].y); + bbox.y1 = tmax(bbox.y1, m_polyLoc[i].y); + } +} + +void RubberDeform::refinePoly(const double rf) +{ + + double refineL = rf <= 0.0 ? avgLength() : rf; + vector tmpv; + int nb = m_polyLoc.size(); + for (int j = 0; j < nb; j++) { + T3DPointD a(m_polyLoc[j]); + T3DPointD b(j == (nb - 1) ? m_polyLoc[0] : m_polyLoc[j + 1]); + tmpv.push_back(a); + double d = tdistance(a, b); + if (d > refineL) { + int n = (int)(d / refineL) + 1; + double q = 1.0 / (double)n; + for (int i = 1; i < n; i++) { + double qq = q * (double)i; + double qq1 = 1.0 - qq; + T3DPointD p(T3DPointD(qq1 * a.x + qq * b.x, qq1 * a.y + qq * b.y, qq1 * a.z + qq * b.z)); + tmpv.push_back(p); + } + } + } + m_polyLoc = tmpv; +} + +// ------------------- SFlashUtils ------------------------------------------- + +void SFlashUtils::computeOutline(const TRegion *region, + TRegionOutline::PointVector &polyline) const +{ + if (!region) + return; + + const double pixelSize = 1.0; + polyline.clear(); + + vector polyline2d; + + int edgeSize = region->getEdgeCount(); + + for (int i = 0; i < edgeSize; i++) { + TEdge &edge = *region->getEdge(i); + stroke2polyline(polyline2d, *edge.m_s, pixelSize, edge.m_w0, edge.m_w1); + } + int pointNumber = polyline2d.size(); + polyline.reserve(pointNumber); + for (int j = 0; j < pointNumber; j++) { + polyline.push_back(T3DPointD(polyline2d[j], 0.0)); + } +} + +void SFlashUtils::computeRegionOutline() +{ + if (!m_r) + return; + + int subRegionNumber = m_r->getSubregionCount(); + TRegionOutline::PointVector app; + + m_ro.m_exterior.clear(); + computeOutline(m_r, app); + + m_ro.m_exterior.push_back(app); + m_ro.m_interior.clear(); + m_ro.m_interior.reserve(subRegionNumber); + for (int i = 0; i < subRegionNumber; i++) { + app.clear(); + computeOutline(m_r->getSubregion(i), app); + m_ro.m_interior.push_back(app); + } + + m_ro.m_bbox = m_r->getBBox(); +} + +void SFlashUtils::PointVector2QuadsArray(const vector &pv, + vector &quadArray, + vector &toBeDeleted, + const bool isRounded) const +{ + vector::const_iterator ipv = pv.begin(); + vector::const_iterator ipve = pv.end(); + int nbPv = pv.size(); + quadArray.clear(); + + if (isRounded) { + if (nbPv <= 2) { + if (nbPv == 1) { + TPointD p0, p1, p2; + p0 = TPointD(pv[0].x, pv[0].y); + p1 = TPointD(pv[0].x, pv[0].y); + p2 = TPointD(pv[0].x, pv[0].y); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } + if (nbPv == 2) { + TPointD p0, p1, p2; + p0 = TPointD(pv[0].x, pv[0].y); + p1 = TPointD((pv[0].x + pv[1].x) / 2, (pv[0].y + pv[1].y) / 2); + p2 = TPointD(pv[1].x, pv[1].y); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } + return; + } + + for (int i = 0; i < (nbPv - 2); i++) { + TPointD p0, p1, p2; + p0 = TPointD((pv[i].x + pv[i + 1].x) / 2, (pv[i].y + pv[i + 1].y) / 2); + p1 = TPointD(pv[i + 1].x, pv[i + 1].y); + p2 = TPointD((pv[i + 1].x + pv[i + 2].x) / 2, (pv[i + 1].y + pv[i + 2].y) / 2); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } + TPointD p0, p1, p2; + p0 = TPointD((pv[nbPv - 2].x + pv[nbPv - 1].x) / 2, (pv[nbPv - 2].y + pv[nbPv - 1].y) / 2); + p1 = TPointD(pv[nbPv - 1].x, pv[nbPv - 1].y); + p2 = TPointD((pv[0].x + pv[1].x) / 2, (pv[0].y + pv[1].y) / 2); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } else { + for (int i = 0; i < (nbPv - 1); i++) { + TPointD p0, p1, p2; + p0 = TPointD(pv[i].x, pv[i].y); + p2 = TPointD(pv[i + 1].x, pv[i + 1].y); + p1 = TPointD((p0.x + p2.x) * 0.5, (p0.y + p2.y) * 0.5); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } + TPointD p0, p1, p2; + p0 = TPointD(pv[nbPv - 1].x, pv[nbPv - 1].y); + p2 = TPointD(pv[0].x, pv[0].y); + p1 = TPointD((p0.x + p2.x) * 0.5, (p0.y + p2.y) * 0.5); + quadArray.push_back(new TQuadratic(p0, p1, p2)); + toBeDeleted.push_back(quadArray.back()); + } +} + +void SFlashUtils::drawRegionOutline(TFlash &flash, const bool isRounded) const +{ + if (!m_r) + return; + + vector> quads; + vector toBeDeleted; + vector quadArray; + PointVector2QuadsArray(*(m_ro.m_exterior.begin()), quadArray, toBeDeleted, isRounded); + quads.push_back(quadArray); + + TRegionOutline::Boundary::const_iterator iinter = m_ro.m_interior.begin(); + TRegionOutline::Boundary::const_iterator iinter_end = m_ro.m_interior.end(); + for (; iinter != iinter_end; iinter++) { + PointVector2QuadsArray(*iinter, quadArray, toBeDeleted, isRounded); + quads.push_back(quadArray); + } + + flash.drawPolygon(quads); + clearPointerContainer(toBeDeleted); +} + +int SFlashUtils::nbDiffVerts(const vector &pv) const +{ + vector lpv; + bool isMissing[4] = {true, true, true, true}; + if (pv.size() == 0) + return 0; + lpv.push_back(pv[0]); + isMissing[0] = false; + for (int i = 1; i < (int)pv.size(); i++) { + bool isDiff = true; + for (int j = 0; j < (int)lpv.size() && isDiff; j++) + isDiff = lpv[j] == pv[i] ? false : isDiff; + if (isDiff) { + lpv.push_back(pv[i]); + isMissing[i] = false; + } + } + return lpv.size(); +} + +/* + TPointD p[3]; + TPixel32 col[2]; +// TPixel32 lc1,lc2; + if ( pv[0]==pv[1] ) { + p[0]=pv[0]; + p[1]=pv[2]; + p[2]=pv[3]; + col[0]=c1; + col[1]=c2; + } else if ( pv[0]==pv[2] ) { + p[0]=pv[3]; + p[1]=pv[0]; + p[2]=pv[1]; + col[0]=c2; + col[1]=c1; + } else if ( pv[0]==pv[3] ) { + p[0]=pv[2]; + p[1]=pv[0]; + p[2]=pv[1]; + col[0]=c2; + col[1]=c1; + } else if ( pv[1]==pv[2] ) { + p[0]=pv[3]; + p[1]=pv[0]; + p[2]=pv[1]; + col[0]=c2; + col[1]=c1; + } else if ( pv[1]==pv[3] ) { + p[0]=pv[2]; + p[1]=pv[0]; + p[2]=pv[1]; + col[0]=c2; + col[1]=c1; + } else if ( pv[2]==pv[3] ) { + p[0]=pv[2]; + p[1]=pv[0]; + p[2]=pv[1]; + col[0]=c2; + col[1]=c1; + } + + + TPointD pt1[4]={pv[0],pv[1],pv[2],pv[3]}; + TPointD pt2[3]={p[0],p[1],p[2]}; + + TPointD uu=p[0]-p[1]; + TPointD up=(p[0]+p[1])*0.5; + uu=normalize(uu); + uu=rotate90(uu); + TPointD up1=up+uu; + TPointD up2=up-uu; + double d1=tdistance(up1,p[2]); + double d2=tdistance(up2,p[2]); + + vector lpv; + if ( d1>d2 ) { + lpv=pv; + } else { + TPointD sw=p[1]; + p[1]=p[2]; + p[2]=sw; + } + + + double a=tdistance(p[1],p[2]); + double b=tdistance(p[2],p[0]); + double c=tdistance(p[0],p[1]); + double x=(b*b-c*c-a*a)/(-2.0*a); + double m=sqrt(c*c-x*x); + TPointD u=p[2]-p[1]; + u=normalize(u); + TPointD q(p[1]+u*x); + + const double flashGrad=16384.0; // size of gradient square + flash.setGradientFill(true,col[0],col[1]); +// TPointD center=pv[0]*0.75+((p[1]+p[2])*0.5)*0.25; + TPointD center=pv[0]; + TPointD e(p[2]-p[1]); + + double angle=rad2degree(atan(e)); + angle= angle<=0 ? 270+angle : angle-90; + TRotation rM(angle); + TTranslation tM(center.x,center.y); + TScale sM(m/flashGrad,2*tmax(x,a-x)/flashGrad); + + flash.setFillStyleMatrix(tM*rM*sM); + vector pp; + pp.push_back(p[0]); + pp.push_back(p[1]); + pp.push_back(p[2]); + + flash.drawPolyline(pp); + + +*/ + +void SFlashUtils::Triangle2Quad(vector &p) const +{ + TPointD e; + int i, j; + i = j = -1; + if (p[0] == p[1]) { + i = 0; + j = 1; + e = p[2] - p[3]; + } else if (p[0] == p[2]) { + + } else if (p[0] == p[3]) { + i = 0; + j = 3; + e = p[2] - p[1]; + } else if (p[1] == p[2]) { + i = 1; + j = 2; + e = p[3] - p[0]; + } else if (p[1] == p[3]) { + } else if (p[2] == p[3]) { + i = 2; + j = 3; + e = p[0] - p[1]; + } + e = normalize(e); + p[j] = p[i] + e * 0.001; +} + +void SFlashUtils::drawGradedPolyline(TFlash &flash, + vector &pvv, + const TPixel32 &c1, + const TPixel32 &c2) const +{ + vector pv; + pv = pvv; + int nbDV = nbDiffVerts(pv); + if (nbDV < 3 || nbDV > 4) + return; + if (nbDV == 3) + Triangle2Quad(pv); + + // Direction Of polyline + TPointD u = pv[0] - pv[1]; + TPointD up = (pv[0] + pv[1]) * 0.5; + u = normalize(u); + u = rotate90(u); + TPointD up1 = up + u; + TPointD up2 = up - u; + double d1 = (tdistance(up1, pv[2]) + tdistance(up1, pv[3])) * 0.5; + double d2 = (tdistance(up2, pv[2]) + tdistance(up2, pv[3])) * 0.5; + + vector lpv; + if (d1 > d2) { + lpv = pv; + } else { + lpv.push_back(pv[1]); + lpv.push_back(pv[0]); + lpv.push_back(pv[3]); + lpv.push_back(pv[2]); + } + + // Transformation of gradient square + const double flashGrad = 16384.0; // size of gradient square + flash.setGradientFill(true, c1, c2, 0); + TPointD p0((lpv[0] + lpv[3]) * 0.5); + TPointD p1((lpv[1] + lpv[2]) * 0.5); + double lv = (tdistance(p0, p1)); + double lh = 0.5 * (tdistance(lpv[0], lpv[3]) + tdistance(lpv[1], lpv[2])); + TPointD center = 0.25 * lpv[0] + 0.25 * lpv[1] + 0.25 * lpv[2] + 0.25 * lpv[3]; + TPointD e(p0 - p1); + + double angle = rad2degree(atan(e)); + angle = angle <= 0 ? 270 + angle : angle - 90; + TRotation rM(angle); + TTranslation tM(center.x, center.y); + TScale sM(lh / (flashGrad), lv / (flashGrad)); + + flash.setFillStyleMatrix(tM * sM * rM); + flash.drawPolyline(pv); +} + +//------------------------------------------------------------ + +//------------------------------------------------------------ +void SFlashUtils::drawGradedRegion(TFlash &flash, + vector &pvv, + const TPixel32 &c1, + const TPixel32 &c2, const TRegion &r) const +{ + vector pv; + pv = pvv; + int nbDV = nbDiffVerts(pv); + if (nbDV < 3 || nbDV > 4) + return; + if (nbDV == 3) + Triangle2Quad(pv); + + // Direction Of polyline + TPointD u = pv[0] - pv[1]; + TPointD up = (pv[0] + pv[1]) * 0.5; + u = normalize(u); + u = rotate90(u); + TPointD up1 = up + u; + TPointD up2 = up - u; + double d1 = (tdistance(up1, pv[2]) + tdistance(up1, pv[3])) * 0.5; + double d2 = (tdistance(up2, pv[2]) + tdistance(up2, pv[3])) * 0.5; + + vector lpv; + if (d1 > d2) { + lpv = pv; + } else { + lpv.push_back(pv[1]); + lpv.push_back(pv[0]); + lpv.push_back(pv[3]); + lpv.push_back(pv[2]); + } + + // Transformation of gradient square + const double flashGrad = 16384.0; // size of gradient square + flash.setGradientFill(true, c1, c2, 0); + TPointD p0((lpv[0] + lpv[3]) * 0.5); + TPointD p1((lpv[1] + lpv[2]) * 0.5); + double lv = (tdistance(p0, p1)); + double lh = 0.5 * (tdistance(lpv[0], lpv[3]) + tdistance(lpv[1], lpv[2])); + TPointD center = 0.25 * lpv[0] + 0.25 * lpv[1] + 0.25 * lpv[2] + 0.25 * lpv[3]; + TPointD e(p0 - p1); + + double angle = rad2degree(atan(e)); + angle = angle <= 0 ? 270 + angle : angle - 90; + TRotation rM(angle); + TTranslation tM(center.x, center.y); + TScale sM(lh / (flashGrad), lv / (flashGrad)); + + flash.setFillStyleMatrix(tM * sM * rM); + flash.drawRegion(r); +} diff --git a/toonz/sources/colorfx/colorfxutils.h b/toonz/sources/colorfx/colorfxutils.h new file mode 100644 index 0000000..eeff9b0 --- /dev/null +++ b/toonz/sources/colorfx/colorfxutils.h @@ -0,0 +1,67 @@ + + +#ifndef COLORFXUTILS_H +#define COLORFXUTILS_H + +#include "tgeometry.h" +#include "tpixel.h" +#include "trandom.h" +#include "tregionoutline.h" +#include "tcurves.h" +using namespace std; + +class TRegion; +class TFlash; + +class RubberDeform +{ + vector *m_pPolyOri; + vector m_polyLoc; + + void deformStep(); + double avgLength(); + void refinePoly(const double rf = -1.0); + void getBBox(TRectD &bbox); + +public: + RubberDeform(); + RubberDeform(vector *pPolyOri, const double rf = -1.0); + void copyLoc2Ori() { *m_pPolyOri = m_polyLoc; }; + void copyOri2Loc() { m_polyLoc = *m_pPolyOri; }; + + virtual ~RubberDeform(); + void deform(const double n); +}; + +class SFlashUtils +{ + void computeOutline(const TRegion *region, TRegionOutline::PointVector &polyline) const; + void PointVector2QuadsArray(const vector &pv, + vector &quadArray, + vector &toBeDeleted, + const bool isRounded) const; + int nbDiffVerts(const vector &pv) const; + void Triangle2Quad(vector &p) const; + +public: + const TRegion *m_r; + TRegionOutline m_ro; + + SFlashUtils(){}; + SFlashUtils(const TRegion *r) : m_r(r){}; + virtual ~SFlashUtils(){}; + + void computeRegionOutline(); + void drawRegionOutline(TFlash &flash, const bool isRounded = true) const; + void drawGradedPolyline(TFlash &flash, + vector &pv, + const TPixel32 &c1, + const TPixel32 &c2) const; + void drawGradedRegion(TFlash &flash, + vector &pv, + const TPixel32 &c1, + const TPixel32 &c2, + const TRegion &r) const; +}; + +#endif diff --git a/toonz/sources/colorfx/rasterstyles.cpp b/toonz/sources/colorfx/rasterstyles.cpp new file mode 100644 index 0000000..d28e2ac --- /dev/null +++ b/toonz/sources/colorfx/rasterstyles.cpp @@ -0,0 +1,239 @@ + + +#include "rasterstyles.h" +#include "trop.h" +#include "tvectorimage.h" +#include "traster.h" +#include "trastercm.h" +#include "tlevel_io.h" +#include "tenv.h" +//#include "tpixelutils.h" + +//************************************************************************************* +// TAirbrushRasterStyle implementation +//************************************************************************************* + +void TAirbrushRasterStyle::loadData(TInputStreamInterface &is) +{ + is >> m_blur; +} + +//----------------------------------------------------------------------------- + +void TAirbrushRasterStyle::saveData(TOutputStreamInterface &os) const +{ + os << m_blur; +} + +//----------------------------------------------------------------------------- + +bool TAirbrushRasterStyle::compute(const Params ¶ms) const +{ + if (m_blur > 0.0) + TRop::blur(params.m_r, params.m_r, m_blur, 0, 0, true); + + return true; +} + +//----------------------------------------------------------------------------- + +TColorStyle *TAirbrushRasterStyle::clone() const +{ + TColorStyle *cs = new TAirbrushRasterStyle(*this); + cs->assignNames(this); + return cs; +} + +//----------------------------------------------------------------------------- + +void TAirbrushRasterStyle::makeIcon(const TDimension &d) +{ + TFilePath dir = TEnv::getStuffDir() + "pixmaps"; + static TRasterP normalIc; + if (normalIc == TRasterP()) + TImageReader::load(dir + "airbrush.bmp", normalIc); + + arrangeIcon(d, normalIc); +} + +/*---------------------------------------------------------------------------*/ + +void TAirbrushRasterStyle::arrangeIcon(const TDimension &d, + const TRasterP &normalIc) +{ + if (normalIc == TRasterP()) { + m_icon = TRaster32P(d); + m_icon->fill(TPixel32::Red); + return; + } + + m_icon = TRasterP(); + + if (d == TDimension(52, 52)) + m_icon = normalIc->clone(); + else { + m_icon = TRaster32P(d); + TRop::resample(m_icon, normalIc, TScale(d.lx / 52.0, d.ly / 52.0)); + } + + m_icon->lock(); + TPixel *buf = (TPixel *)m_icon->getRawData(); + TPixel color = getMainColor(); + double rd = (color.r - color.m) / 255.0; + double gd = (color.g - color.m) / 255.0; + double bd = (color.b - color.m) / 255.0; + + assert(m_icon->getWrap() == m_icon->getSize().lx); + + for (int i = 0; i < m_icon->getSize().lx * m_icon->getSize().ly; i++, buf++) { + assert(buf->r == buf->g && buf->r == buf->b); + + if (buf->r < 255) { + if (buf->r == 0) + *buf = color; + else { + int val = 255 - buf->r; + buf->r = (UCHAR)(val * rd + 255); + buf->g = (UCHAR)(val * gd + 255); + buf->b = (UCHAR)(val * bd + 255); + } + } + } + m_icon->unlock(); +} + +//************************************************************************************* +// TBlendRasterStyle implementation +//************************************************************************************* + +TColorStyle *TBlendRasterStyle::clone() const +{ + return new TBlendRasterStyle(*this); +} + +//----------------------------------------------------------------------------- + +bool TBlendRasterStyle::compute(const Params ¶ms) const +{ + int i, j; + TRasterGR8P r = (TRasterGR8P)params.m_r; + assert(r); + + double factor = computeFactor(params); + + if (m_blur > 0.0) + TRop::blur(params.m_r, params.m_r, m_blur, 0, 0, true); + + r->lock(); + for (i = 0; i < r->getLy(); i++) { + TPixelGR8 *bufGr = r->pixels(i); + for (j = 0; j < r->getLx(); j++, bufGr++) + if (bufGr->value > 0) { + double val = bufGr->value * factor + 0.5; + bufGr->value = (UCHAR)((val > 255) ? 255 : val); + } + } + r->unlock(); + return true; +} + +//----------------------------------------------------------------------------- +namespace +{ +bool inline doIsBorderPix(int index, TPixelCM32 *pix, TPixelGR8 *pixGr, int paint) +{ + return (pix->getPaint() != paint && (pix->isPurePaint() || pixGr->value != 0)); +} + +//----------------------------------------------------------------------------- + +bool inline isBorderPix(int index, TPixelCM32 *pix, int wrap, TPixelGR8 *pixGr, int wrapGr, int color) +{ + return doIsBorderPix(index, pix + 1, pixGr + 1, color) || + doIsBorderPix(index, pix - 1, pixGr - 1, color) || + doIsBorderPix(index, pix + wrap, pixGr + wrapGr, color) || + doIsBorderPix(index, pix - wrap, pixGr - wrapGr, color); +} +} + +/*---------------------------------------------------------------------------*/ + +void TBlendRasterStyle::makeIcon(const TDimension &d) +{ + TFilePath dir = TEnv::getStuffDir() + "pixmaps"; + static TRasterP normalIc; + if (normalIc == TRasterP()) + TImageReader::load(dir + "blend.bmp", normalIc); + + arrangeIcon(d, normalIc); +} +//----------------------------------------------------------------------------- + +double TBlendRasterStyle::computeFactor(const Params ¶ms) const +{ + TRasterCM32P rCm = (TRasterCM32P)params.m_rOrig; + TRasterGR8P rGr = (TRasterGR8P)params.m_r; + TPixelGR8 *bufGr; + TPixelCM32 *bufCmap; + int i, j; + int wr = params.m_rOrig->getWrap(); + int wrGr = params.m_r->getWrap(); + int num_lines = 0; + double tot_sum = 0; + + //interactive! + //if (Factor == 0) + rGr->lock(); + rCm->lock(); + for (i = 1; i < params.m_r->getLy() - 1; i++) { + int line_sum = 0, num_points = 0; + bufGr = rGr->pixels(i) + 1; + bufCmap = rCm->pixels(i) + 1; + for (j = 1; j < rGr->getLx() - 1; j++, bufGr++, bufCmap++) { + if (bufGr->value > 0) { + int paint = bufCmap->getPaint(); + if ((bufCmap->isPurePaint() || bufGr->value != 0) && + isBorderPix(params.m_colorIndex, bufCmap, wr, bufGr, wrGr, paint)) { + line_sum += troundp(bufGr->value); + num_points++; + } + } + } + rGr->unlock(); + rCm->unlock(); + if (num_points != 0) { + tot_sum += ((float)line_sum / ((float)num_points * 255)); + num_lines++; + } + } + if (num_lines == 0) + return 0; + + return num_lines / tot_sum; +} + +//************************************************************************************* +// TNoColorRasterStyle implementation +//************************************************************************************* + +void TNoColorRasterStyle::makeIcon(const TDimension &d) +{ + TFilePath dir = TEnv::getStuffDir() + "pixmaps"; + static TRasterP normalIc; + if (normalIc == TRasterP()) + TImageReader::load(dir + "markup.bmp", normalIc); + + if (normalIc == TRasterP()) { + m_icon = TRaster32P(d); + m_icon->fill(TPixel32::Red); + return; + } + + m_icon = TRasterP(); + if (d == TDimension(52, 52) && normalIc != TRasterP()) + m_icon = normalIc->clone(); + else { + m_icon = TRaster32P(d); + TRop::resample(m_icon, normalIc, TScale(d.lx / 52.0, d.ly / 52.0)); + } +} diff --git a/toonz/sources/colorfx/rasterstyles.h b/toonz/sources/colorfx/rasterstyles.h new file mode 100644 index 0000000..007e1fe --- /dev/null +++ b/toonz/sources/colorfx/rasterstyles.h @@ -0,0 +1,186 @@ + + +#ifndef _RASTERSTYLES_H_ +#define _RASTERSTYLES_H_ + +#include "tcolorstyles.h" + +#include "traster.h" + +#include + +class TStroke; +class TRegion; +class TStrokeProp; +class TRegionProp; +class TInputStreamInterface; +class TOutputStreamInterface; + +//============================================================================= + +class TAirbrushRasterStyle : public TColorStyle, public TRasterStyleFx +{ +protected: + TPixel32 m_color; + double m_blur; + +public: + TAirbrushRasterStyle(const TPixel32 &color, double blur) + : m_color(color), m_blur(blur) {} + + TColorStyle *clone() const; + +public: + // n.b. per un plain color: isRasterStyle() == true, ma getRasterStyleFx() = 0 + + TStrokeProp *makeStrokeProp(const TStroke *stroke) { return 0; } + TRegionProp *makeRegionProp(const TRegion *region) { return 0; } + TRasterStyleFx *getRasterStyleFx() { return this; } + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return false; } + bool isRasterStyle() const { return true; } + void getEnlargement(int &borderIn, int &borderOut) const + { + borderIn = tceil(2 * m_blur); + borderOut = tceil(m_blur); + } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getColorParamCount() const { return 1; } + TPixel32 getColorParamValue(int index) const { return m_color; } + void setColorParamValue(int index, const TPixel32 &color) + { + m_color = color; + } + + virtual QString getDescription() const { return QCoreApplication::translate("TAirbrushRasterStyle", "Airbrush"); } + + int getParamCount() const { return 1; } + TColorStyle::ParamType getParamType(int index) const + { + assert(index == 0); + return TColorStyle::DOUBLE; + } + + QString getParamNames(int index) const + { + assert(index == 0); + return QCoreApplication::translate("TAirbrushRasterStyle", "Blur value"); + } + void getParamRange(int index, double &min, double &max) const + { + assert(index == 0); + min = 0; + max = 30; + } + double getParamValue(TColorStyle::double_tag, int index) const + { + assert(index == 0); + return m_blur; + } + void setParamValue(int index, double value) + { + assert(index == 0); + m_blur = value; + } + + void invalidateIcon(); + + //const TRaster32P &getIcon(const TDimension &d) {assert(false);return (TRaster32P)0;} + + TPixel32 getAverageColor() const { return m_color; } + + int getTagId() const { return 1150; } + + bool isInkStyle() const { return true; } + bool isPaintStyle() const { return false; } + + bool compute(const Params ¶ms) const; + +protected: + virtual void makeIcon(const TDimension &d); + + void arrangeIcon(const TDimension &d, const TRasterP &normalIc); + + void loadData(TInputStreamInterface &); + void saveData(TOutputStreamInterface &) const; + + // per la compatibilita' con il passato + void loadData(int oldId, TInputStreamInterface &){}; +}; + +//============================================================================= + +class TBlendRasterStyle : public TAirbrushRasterStyle +{ +public: + TBlendRasterStyle(const TPixel32 &color, double blur) + : TAirbrushRasterStyle(color, blur) {} + TColorStyle *clone() const; + + int getTagId() const { return 1160; } + + virtual QString getDescription() const { return QCoreApplication::translate("TBlendRasterStyle", "Blend"); } + + void makeIcon(const TDimension &d); + + bool compute(const TRasterStyleFx::Params ¶ms) const; + +private: + double computeFactor(const TRasterStyleFx::Params ¶ms) const; +}; + +//============================================================================= + +class TNoColorRasterStyle : public TColorStyle, TRasterStyleFx +{ +public: + TNoColorRasterStyle() {} + TColorStyle *clone() const { return new TNoColorRasterStyle(*this); } + + // n.b. per un plain color: isRasterStyle() == true, ma getRasterStyleFx() = 0 + + TStrokeProp *makeStrokeProp(const TStroke *stroke) { return 0; } + TRegionProp *makeRegionProp(const TRegion *region) { return 0; } + TRasterStyleFx *getRasterStyleFx() { return this; } + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return false; } + bool isRasterStyle() const { return true; } + + virtual QString getDescription() const { return QCoreApplication::translate("TNoColorRasterStyle", "Markup"); } + + bool hasMainColor() { return false; } + //TPixel32 getMainColor() const {return m_color;} + //void setMainColor(const TPixel32 &color) {m_color = color;} + + int getColorParamCount() const { return 0; } + TPixel32 getColorParamValue(int index) const + { + assert(false); + return TPixel32(); + } + void setColorParamValue(int index, const TPixel32 &color) { assert(false); } + + int getTagId() const { return 1151; } + + bool isInkStyle() const { return true; } + bool isPaintStyle() const { return true; } + + bool compute(const Params ¶ms) const { return false; } + +protected: + void makeIcon(const TDimension &d); + + void loadData(TInputStreamInterface &){}; + void saveData(TOutputStreamInterface &) const {}; + + // per la compatibilita' con il passato + void loadData(int oldId, TInputStreamInterface &){}; +}; + +#endif diff --git a/toonz/sources/colorfx/regionstyles.cpp b/toonz/sources/colorfx/regionstyles.cpp new file mode 100644 index 0000000..57fa739 --- /dev/null +++ b/toonz/sources/colorfx/regionstyles.cpp @@ -0,0 +1,5231 @@ + + +#include "regionstyles.h" +#include "tgl.h" +#include "tcolorfunctions.h" +#include "trandom.h" +//#include "tsystem.h" +//#include "tvectorrenderdata.h" +#include "colorfxutils.h" +//#include "tgl.h" +//#include "tregionoutline.h" +//#include "tpalette.h" +//#include "tvectorimage.h" +//#include "tstroke.h" +#include "tflash.h" +#include "tregion.h" +#include "tcurves.h" +//#include "drawutil.h" +#include "tmathutil.h" +#include "tstencilcontrol.h" + +//*************************************************************************** +// Local namesapce stuff +//*************************************************************************** + +namespace +{ + +const double pi2 = TConsts::pi * 2.0; + +} // namespace + +//*************************************************************************** +// MovingModifier implementation +//*************************************************************************** + +TOutlineStyle::RegionOutlineModifier *MovingModifier::clone() const +{ + return new MovingModifier(*this); +} + +//------------------------------------------------------------ + +void MovingModifier::modify(TRegionOutline &outline) const +{ + TRegionOutline::Boundary::iterator regIt = outline.m_exterior.begin(); + TRegionOutline::Boundary::iterator regItEnd = outline.m_exterior.end(); + + TRegionOutline::PointVector::iterator pIt; + TRegionOutline::PointVector::iterator pItEnd; + + for (; regIt != regItEnd; ++regIt) { + pIt = regIt->begin(); + pItEnd = regIt->end(); + + for (; pIt != pItEnd; ++pIt) { + pIt->x += m_move.x; + pIt->y += m_move.y; + } + } + + regIt = outline.m_interior.begin(); + regItEnd = outline.m_interior.end(); + for (; regIt != regItEnd; ++regIt) { + pIt = regIt->begin(); + pItEnd = regIt->end(); + + for (; pIt != pItEnd; ++pIt) { + pIt->x += m_move.x; + pIt->y += m_move.y; + } + } +} + +//*************************************************************************** +// MovingSolidColor implementation +//*************************************************************************** + +MovingSolidColor::MovingSolidColor(const TPixel32 &color, const TPointD &move) + : TSolidColorStyle(color) +{ + m_regionOutlineModifier = new MovingModifier(move); +} + +//------------------------------------------------------------ + +TColorStyle *MovingSolidColor::clone() const +{ + return new MovingSolidColor(*this); +} + +//------------------------------------------------------------ + +void MovingSolidColor::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + delete m_regionOutlineModifier; + MovingModifier *mov = new MovingModifier(TPointD()); + mov->loadData(is); + m_regionOutlineModifier = mov; +} + +//------------------------------------------------------------ + +void MovingSolidColor::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + + assert(m_regionOutlineModifier); + ((MovingModifier *)m_regionOutlineModifier)->saveData(os); +} + +//------------------------------------------------------------ + +int MovingSolidColor::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType MovingSolidColor::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString MovingSolidColor::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + + return index == 0 ? QCoreApplication::translate("MovingSolidColor", "Horiz Offset") : QCoreApplication::translate("MovingSolidColor", "Vert Offset"); +} + +//----------------------------------------------------------------------------- + +void MovingSolidColor::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + min = -100.0; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double MovingSolidColor::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + + if (index) + return ((MovingModifier *)m_regionOutlineModifier)->getMovePoint().y; + else + return ((MovingModifier *)m_regionOutlineModifier)->getMovePoint().x; +} + +//----------------------------------------------------------------------------- + +void MovingSolidColor::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + + TPointD oldMove = ((MovingModifier *)m_regionOutlineModifier)->getMovePoint(); + + if (!index) { + if (oldMove.x != value) { + delete m_regionOutlineModifier; + oldMove.x = value; + m_regionOutlineModifier = new MovingModifier(oldMove); + updateVersionNumber(); + } + } else { + if (oldMove.y != value) { + delete m_regionOutlineModifier; + oldMove.y = value; + m_regionOutlineModifier = new MovingModifier(oldMove); + updateVersionNumber(); + } + } +} + +//------------------------------------------------------------ + +void MovingSolidColor::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + TSolidColorStyle::drawRegion(cf, true, boundary); +} + +//------------------------------------------------------------ + +void MovingSolidColor::drawRegion(TFlash &flash, const TRegion *r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + m_regionOutlineModifier->modify(rdf.m_ro); + flash.setFillColor(getMainColor()); + rdf.drawRegionOutline(flash, false); +} + +//*************************************************************************** +// ShadowStyle implementation +//*************************************************************************** + +ShadowStyle::ShadowStyle(const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection, + double len, + double density) + : TSolidColorStyle(bgColor), m_shadowColor(shadowColor), m_shadowDirection(normalize(shadowDirection)), m_len(len), m_density(density) +{ +} + +//------------------------------------------------------------ + +TColorStyle *ShadowStyle::clone() const +{ + return new ShadowStyle(*this); +} + +//------------------------------------------------------------ + +void ShadowStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_shadowDirection.x >> m_shadowDirection.y; + is >> m_density; + is >> m_shadowColor; + is >> m_len; +} + +//------------------------------------------------------------ + +void ShadowStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_shadowDirection.x << m_shadowDirection.y; + os << m_density; + os << m_shadowColor; + os << m_len; +} + +//------------------------------------------------------------ + +int ShadowStyle::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType ShadowStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString ShadowStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + return QCoreApplication::translate("ShadowStyle", "Angle"); + case 1: + return QCoreApplication::translate("ShadowStyle", "Density"); + case 2: + return QCoreApplication::translate("ShadowStyle", "Length"); + + default: + return QString(); + } + + assert(0); + return QString(); +} + +//----------------------------------------------------------------------------- + +void ShadowStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + min = 0.0; + max = 360.0; + break; + case 1: + min = 0.0; + max = 1.0; + break; + case 2: + min = 0.0; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double ShadowStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + + double degree; + + switch (index) { + case 0: + degree = asin(m_shadowDirection.y); + if (m_shadowDirection.x < 0) + degree = TConsts::pi - degree; + if (degree < 0) + degree += pi2; + return degree * TConsts::invOf_pi_180; + + case 1: + return m_density; + + case 2: + return m_len; + } + + assert(0); + return 0.0; +} + +//----------------------------------------------------------------------------- + +void ShadowStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + double degree; + + switch (index) { + case 0: + degree = value * TConsts::pi_180; + m_shadowDirection.x = cos(degree); + m_shadowDirection.y = sin(degree); + break; + + case 1: + m_density = value; + break; + + case 2: + m_len = value; + break; + } +} + +//------------------------------------------------------------ + +void ShadowStyle::drawPolyline(const TColorFunction *cf, vector &polyline, TPointD shadowDirection) const +{ + int i; + int stepNumber; + double distance; + + TPointD v1, v2, diff, midPoint, ratio; + double len; + + TPixel32 color; + if (cf) + color = (*(cf))(m_shadowColor); + else + color = m_shadowColor; + + tglColor(color); + + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + + TRegionOutline::PointVector::iterator it; + TRegionOutline::PointVector::iterator it_b = polyline.begin(); + TRegionOutline::PointVector::iterator it_e = polyline.end(); + + v1.x = polyline.back().x; + v1.y = polyline.back().y; + + for (it = it_b; it != it_e; ++it) { + v2.x = it->x; + v2.y = it->y; + if (v1 == v2) + continue; + + diff = normalize(rotate90(v2 - v1)); + len = diff * shadowDirection; + + if (len > 0) { + distance = tdistance(v1, v2) * m_density; + + ratio = (v2 - v1) * (1.0 / distance); + midPoint = v1; + stepNumber = (int)distance; + + for (i = 0; i < stepNumber; i++) { + glBegin(GL_LINE_STRIP); + + tglColor(TPixel32(color.r, color.g, color.b, 0)); + tglVertex(midPoint); + + tglColor(color); + tglVertex(midPoint + (shadowDirection * len * m_len * 0.5)); + + tglColor(TPixel32(color.r, color.g, color.b, 0)); + tglVertex(midPoint + (shadowDirection * len * m_len)); + + midPoint += ratio; + + glEnd(); + } + } + + v1 = v2; + } + //tglColor(TPixel32::White); +} + +//------------------------------------------------------------ + +void ShadowStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, + TRegionOutline ®ionOutline) const +{ + TStencilControl *stenc = TStencilControl::instance(); + + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + ////stenc->beginMask(); + /* + glBegin(GL_QUADS); + glVertex2d (regionOutline.m_bbox.getP00().x, regionOutline.m_bbox.getP00().y); + glVertex2d (regionOutline.m_bbox.getP01().x, regionOutline.m_bbox.getP01().y); + glVertex2d (regionOutline.m_bbox.getP11().x, regionOutline.m_bbox.getP11().y); + glVertex2d (regionOutline.m_bbox.getP10().x, regionOutline.m_bbox.getP10().y); + glEnd(); + */ + ////stenc->endMask(); + ////stenc->enableMask(TStencilControl::SHOW_INSIDE); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); + appStyle.drawRegion(0, false, regionOutline); + } else { + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, regionOutline); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = regionOutline.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = regionOutline.m_exterior.end(); + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) + drawPolyline(cf, *regions_it, m_shadowDirection); + + stenc->enableMask(TStencilControl::SHOW_OUTSIDE); + + regions_it_b = regionOutline.m_interior.begin(); + regions_it_e = regionOutline.m_interior.end(); + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) + drawPolyline(cf, *regions_it, -m_shadowDirection); + + //tglColor(TPixel32::White); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +/* +int ShadowStyle::drawPolyline(TFlash& flash, vector &polyline, + TPointD shadowDirection, const bool isDraw) const +{ + int i; + int stepNumber; + double distance; + + TPointD v1,v2,diff,midPoint,ratio; + double len; + + + TRegionOutline::PointVector::iterator it; + TRegionOutline::PointVector::iterator it_b = polyline.begin(); + TRegionOutline::PointVector::iterator it_e = polyline.end(); + + + vector segmentArray; + + v1.x = polyline.back().x; + v1.y = polyline.back().y; + + for(it = it_b; it!= it_e; ++it) + { + v2.x = it->x; + v2.y = it->y; + if (v1==v2) + continue; + + + diff = normalize(rotate90(v2-v1)); + len=diff*shadowDirection; + + if(len>0) + { + distance = tdistance(v1,v2)*m_density; + + ratio= (v2-v1)*(1.0/distance); + midPoint=v1; + stepNumber= (int)distance; + + for(i=0; i sa; + + TPointD p0=midPoint; + TPointD p1=midPoint+(shadowDirection*len*m_len*0.5); + TPointD p2=midPoint+(shadowDirection*len*m_len); + + segmentArray.push_back(TSegment(p1,p0)); + segmentArray.push_back(TSegment(p1,p2)); + + midPoint += ratio; + } + + } + + v1=v2; + } + + + if ( isDraw && segmentArray.size()>0 ) { + flash.setLineColor(m_shadowColor); + flash.drawSegments(segmentArray, true); + } + + if ( segmentArray.size()>0 ) + return 1; + return 0; +} + +void ShadowStyle::drawRegion( TFlash& flash, const TRegion* r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = rdf.m_ro.m_exterior->begin(); + TRegionOutline::Boundary::iterator regions_it_e = rdf.m_ro.m_exterior->end(); + + +// In the GL version the shadow lines are not croped into the filled region. +// This is the reason why I don't calculate the number of shadow lines. +// int nbDraw=0; +// for( regions_it = regions_it_b ; regions_it!= regions_it_e; ++regions_it) +// nbDraw+=drawPolyline(flash,*regions_it, m_shadowDirection,false); + +// regions_it_b = rdf.m_ro.m_interior->begin(); +// regions_it_e = rdf.m_ro.m_interior->end(); +// for( regions_it = regions_it_b ; regions_it!= regions_it_e; ++regions_it) +// nbDraw+=drawPolyline(flash,*regions_it,-m_shadowDirection,false); + + +// Only the bbox rectangle is croped. + flash.drawRegion(*r,1); + flash.setFillColor(getMainColor()); + flash.drawRectangle(rdf.m_ro.m_bbox); + + regions_it_b = rdf.m_ro.m_exterior->begin(); + regions_it_e = rdf.m_ro.m_exterior->end(); + for( regions_it = regions_it_b ; regions_it!= regions_it_e; ++regions_it) + drawPolyline(flash,*regions_it, m_shadowDirection); + + regions_it_b = rdf.m_ro.m_interior->begin(); + regions_it_e = rdf.m_ro.m_interior->end(); + for( regions_it = regions_it_b ; regions_it!= regions_it_e; ++regions_it) + drawPolyline(flash,*regions_it,-m_shadowDirection); + + +} +*/ + +//------------------------------------------------------------ + +TPixel32 ShadowStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_shadowColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void ShadowStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_shadowColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void ShadowStyle::makeIcon(const TDimension &d) +{ + double oldVal = getParamValue(TColorStyle::double_tag(), 1); + setParamValue(1, oldVal * 0.25); + TColorStyle::makeIcon(d); + setParamValue(1, oldVal); +} + +//*************************************************************************** +// ShadowStyle2 implementation +//*************************************************************************** + +ShadowStyle2::ShadowStyle2(const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection, + double shadowLength) + : TSolidColorStyle(bgColor), m_shadowColor(shadowColor), m_shadowLength(shadowLength), m_shadowDirection(normalize(shadowDirection)) +{ +} + +//------------------------------------------------------------ + +TColorStyle *ShadowStyle2::clone() const +{ + return new ShadowStyle2(*this); +} + +//------------------------------------------------------------ + +void ShadowStyle2::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_shadowDirection.x >> m_shadowDirection.y; + is >> m_shadowLength; + is >> m_shadowColor; +} + +//------------------------------------------------------------ + +void ShadowStyle2::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_shadowDirection.x << m_shadowDirection.y; + os << m_shadowLength; + os << m_shadowColor; +} + +//------------------------------------------------------------ + +int ShadowStyle2::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType ShadowStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString ShadowStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + + return index == 0 ? QCoreApplication::translate("ShadowStyle2", "Angle") : QCoreApplication::translate("ShadowStyle2", "Size"); +} + +//----------------------------------------------------------------------------- + +void ShadowStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 0.0; + max = 360.0; + } else { + min = 0.0; + max = 500.0; + } +} + +//----------------------------------------------------------------------------- + +double ShadowStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + + if (index == 1) + return m_shadowLength; + + double degree = asin(m_shadowDirection.y); + if (m_shadowDirection.x < 0) + degree = TConsts::pi - degree; + + if (degree < 0) + degree += pi2; + + return degree * TConsts::invOf_pi_180; +} + +//----------------------------------------------------------------------------- + +int nbDiffVerts(const vector &pv) +{ + vector lpv; + bool isMissing[4] = {true, true, true, true}; + if (pv.size() == 0) + return 0; + lpv.push_back(pv[0]); + isMissing[0] = false; + for (int i = 1; i < (int)pv.size(); i++) { + bool isDiff = true; + for (int j = 0; j < (int)lpv.size() && isDiff; j++) + isDiff = lpv[j] == pv[i] ? false : isDiff; + if (isDiff) { + lpv.push_back(pv[i]); + isMissing[i] = false; + } + } + return lpv.size(); +} + +void ShadowStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + + if (index == 1) { + m_shadowLength = value; + } else { + double degree = value * TConsts::pi_180; + m_shadowDirection.x = cos(degree); + m_shadowDirection.y = sin(degree); + } +} + +//------------------------------------------------------------ + +namespace +{ + +void drawShadowLine(TPixel32 shadowColor, TPixel32 color, TPointD v1, TPointD v2, TPointD diff1, TPointD diff2) +{ + v1 = v1 + diff1; + v2 = v2 + diff2; + diff1 = -diff1; + diff2 = -diff2; + + double r1, r2; + double t = 0.0; + + glBegin(GL_QUAD_STRIP); + + for (; t <= 1; t += 0.1) { + r1 = t * t * t; + r2 = 1 - r1; + + TPixel32 c((int)(color.r * r2 + shadowColor.r * r1), + (int)(color.g * r2 + shadowColor.g * r1), + (int)(color.b * r2 + shadowColor.b * r1), + (int)(color.m * r2 + shadowColor.m * r1)); + tglColor(c); + tglVertex(v1 + t * diff1); + tglVertex(v2 + t * diff2); + } + + glEnd(); +} + +int drawShadowLine(TFlash &flash, TPixel32 shadowColor, TPixel32 color, + TPointD v1, TPointD v2, TPointD diff1, TPointD diff2, + const bool isDraw = true) +{ + int nbDraw = 0; + + v1 = v1 + diff1; + v2 = v2 + diff2; + diff1 = -diff1; + diff2 = -diff2; + + TPointD vv1, vv2, ovv1, ovv2; + TPixel32 oc; + double r1, r2; + double t = 0.0; + bool isFirst = true; + flash.setThickness(0.0); + SFlashUtils sfu; + for (; t <= 1; t += 0.1) { + if (isFirst) { + r1 = t * t * t; + r2 = 1 - r1; + oc = TPixel32((int)(color.r * r2 + shadowColor.r * r1), + (int)(color.g * r2 + shadowColor.g * r1), + (int)(color.b * r2 + shadowColor.b * r1), + (int)(color.m * r2 + shadowColor.m * r1)); + ovv1 = v1 + t * diff1; + ovv2 = v2 + t * diff2; + isFirst = false; + } else { + r1 = t * t * t; + r2 = 1 - r1; + TPixel32 c((int)(color.r * r2 + shadowColor.r * r1), + (int)(color.g * r2 + shadowColor.g * r1), + (int)(color.b * r2 + shadowColor.b * r1), + (int)(color.m * r2 + shadowColor.m * r1)); + vv1 = (v1 + t * diff1); + vv2 = (v2 + t * diff2); + + vector pv; + pv.push_back(ovv1); + pv.push_back(ovv2); + pv.push_back(vv2); + pv.push_back(vv1); + + int nbDV = nbDiffVerts(pv); + if (nbDV >= 3 && nbDV <= 4) + nbDraw++; + + if (isDraw) + sfu.drawGradedPolyline(flash, pv, oc, c); + + oc = c; + ovv1 = vv1; + ovv2 = vv2; + } + } + return nbDraw; +} +} + +//------------------------------------------------------------ + +void ShadowStyle2::drawPolyline(const TColorFunction *cf, const vector &polyline, TPointD shadowDirection) const +{ + if (polyline.empty()) + return; + TPointD v0, v1, diff; + double len1, len2; + + TPixel32 color, shadowColor; + + if (cf) + color = (*(cf))(TSolidColorStyle::getMainColor()); + else + color = TSolidColorStyle::getMainColor(); + + if (cf) + shadowColor = (*(cf))(m_shadowColor); + else + shadowColor = m_shadowColor; + + tglColor(shadowColor); + + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + TRegionOutline::PointVector::const_iterator it; + TRegionOutline::PointVector::const_iterator it_b = polyline.begin(); + TRegionOutline::PointVector::const_iterator it_e = polyline.end(); + + int size = polyline.size(); + vector lens(size); + v0.x = polyline.back().x; + v0.y = polyline.back().y; + int count = 0; + for (it = it_b; it != it_e; ++it) { + v1.x = it->x; + v1.y = it->y; + if (v1 != v0) { + diff = normalize(rotate90(v1 - v0)); + len1 = diff * shadowDirection; + if (len1 < 0) + len1 = 0; + lens[count++] = len1; + } else + lens[count++] = 0; + + v0 = v1; + } + + double firstVal = lens.front(); + for (count = 0; count != size - 1; count++) { + lens[count] = (lens[count] + lens[count + 1]) * 0.5; + } + lens[size - 1] = (lens[size - 1] + firstVal) * 0.5; + + for (count = 0; count != size - 1; count++) { + v0.x = polyline[count].x; + v0.y = polyline[count].y; + v1.x = polyline[count + 1].x; + v1.y = polyline[count + 1].y; + len1 = lens[count]; + len2 = lens[count + 1]; + + if (v0 != v1 && len1 >= 0 && len2 >= 0 && (len1 + len2) > 0) + drawShadowLine(shadowColor, color, v0, v1, shadowDirection * len1 * m_shadowLength, shadowDirection * len2 * m_shadowLength); + } + v0.x = polyline[count].x; + v0.y = polyline[count].y; + v1.x = polyline.front().x; + v1.y = polyline.front().y; + len1 = lens[count]; + len2 = lens[0]; + if (v0 != v1 && len1 >= 0 && len2 >= 0 && (len1 + len2) > 0) + drawShadowLine(shadowColor, color, v0, v1, shadowDirection * len1 * m_shadowLength, shadowDirection * len2 * m_shadowLength); + + //tglColor(TPixel32::White); +} + +//------------------------------------------------------------ + +TPixel32 ShadowStyle2::getColorParamValue(int index) const +{ + return index == 0 ? m_shadowColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void ShadowStyle2::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_shadowColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void ShadowStyle2::drawRegion(const TColorFunction *cf, const bool antiAliasing, + TRegionOutline &boundary) const +{ + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = boundary.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = boundary.m_exterior.end(); + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) + drawPolyline(cf, *regions_it, m_shadowDirection); + + //tglColor(TPixel32::White); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +int ShadowStyle2::drawPolyline(TFlash &flash, vector &polyline, + TPointD shadowDirection, const bool isDraw) const +{ + int nbDraw = 0; + + TPointD v0, v1, diff; + double len1, len2; + TPixel32 color, shadowColor; + color = TSolidColorStyle::getMainColor(); + shadowColor = m_shadowColor; + + TRegionOutline::PointVector::iterator it; + TRegionOutline::PointVector::iterator it_b = polyline.begin(); + TRegionOutline::PointVector::iterator it_e = polyline.end(); + + int size = polyline.size(); + vector lens(size); + v0.x = polyline.back().x; + v0.y = polyline.back().y; + int count = 0; + for (it = it_b; it != it_e; ++it) { + v1.x = it->x; + v1.y = it->y; + if (v1 != v0) { + diff = normalize(rotate90(v1 - v0)); + len1 = diff * shadowDirection; + if (len1 < 0) + len1 = 0; + lens[count++] = len1; + } else + lens[count++] = 0; + + v0 = v1; + } + + double firstVal = lens.front(); + for (count = 0; count != size - 1; count++) { + lens[count] = (lens[count] + lens[count + 1]) * 0.5; + } + lens[size - 1] = (lens[size - 1] + firstVal) * 0.5; + + for (count = 0; count != size - 1; count++) { + v0.x = polyline[count].x; + v0.y = polyline[count].y; + v1.x = polyline[count + 1].x; + v1.y = polyline[count + 1].y; + len1 = lens[count]; + len2 = lens[count + 1]; + + if (v0 != v1 && len1 >= 0 && len2 >= 0 && (len1 + len2) > 0) + nbDraw += drawShadowLine(flash, shadowColor, color, v0, v1, shadowDirection * len1 * m_shadowLength, + shadowDirection * len2 * m_shadowLength, isDraw); + } + v0.x = polyline[count].x; + v0.y = polyline[count].y; + v1.x = polyline.front().x; + v1.y = polyline.front().y; + len1 = lens[count]; + len2 = lens[0]; + if (v0 != v1 && len1 >= 0 && len2 >= 0 && (len1 + len2) > 0) + nbDraw += drawShadowLine(flash, shadowColor, color, v0, v1, shadowDirection * len1 * m_shadowLength, + shadowDirection * len2 * m_shadowLength, isDraw); + + return nbDraw; +} + +//------------------------------------------------------------ + +void ShadowStyle2::drawRegion(TFlash &flash, const TRegion *r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = rdf.m_ro.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = rdf.m_ro.m_exterior.end(); + + int nbDraw = 0; + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) + nbDraw += drawPolyline(flash, *regions_it, m_shadowDirection, false); + + flash.drawRegion(*r, nbDraw + 1); + flash.setFillColor(getMainColor()); + flash.drawRectangle(rdf.m_ro.m_bbox); + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) + drawPolyline(flash, *regions_it, m_shadowDirection); +} + +//*************************************************************************** +// RubberModifier implementation +//*************************************************************************** + +TOutlineStyle::RegionOutlineModifier *RubberModifier::clone() const +{ + return new RubberModifier(*this); +} + +//------------------------------------------------------------ + +void RubberModifier::modify(TRegionOutline &outline) const +{ + double deformSize = 40.0 + (100 - m_deform) * 0.60; + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = outline.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = outline.m_exterior.end(); + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) { + RubberDeform rd(®ions_it[0]); + rd.deform(deformSize); + } + + regions_it_b = outline.m_interior.begin(); + regions_it_e = outline.m_interior.end(); + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) { + RubberDeform rd(®ions_it[0]); + rd.deform(deformSize); + } +} + +//*************************************************************************** +// TRubberFillStyle implementation +//*************************************************************************** + +TRubberFillStyle::TRubberFillStyle(const TPixel32 &color, double deform) + : TSolidColorStyle(color) +{ + m_regionOutlineModifier = new RubberModifier(deform); +} + +//------------------------------------------------------------ + +TColorStyle *TRubberFillStyle::clone() const +{ + return new TRubberFillStyle(*this); +} + +//------------------------------------------------------------ + +void TRubberFillStyle::makeIcon(const TDimension &d) +{ + // Saves the values of member variables and sets the right icon values + + RubberModifier *prm = (RubberModifier *)(m_regionOutlineModifier); + double LDeform = prm->getDeform(); + prm->setDeform(LDeform); + + TColorStyle::makeIcon(d); + + // Loads the original values + prm->setDeform(LDeform); +} + +//------------------------------------------------------------ + +int TRubberFillStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TRubberFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TRubberFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TRubberFillStyle", "Intensity"); +} + +//----------------------------------------------------------------------------- + +void TRubberFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 0.; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TRubberFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return ((RubberModifier *)m_regionOutlineModifier)->getDeform(); +} + +//----------------------------------------------------------------------------- + +void TRubberFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + + double oldDeform = ((RubberModifier *)m_regionOutlineModifier)->getDeform(); + + if (oldDeform != value) { + delete m_regionOutlineModifier; + m_regionOutlineModifier = new RubberModifier(value); + updateVersionNumber(); + } +} + +//------------------------------------------------------------ + +void TRubberFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + delete m_regionOutlineModifier; + RubberModifier *rub = new RubberModifier(0.0); + rub->loadData(is); + m_regionOutlineModifier = rub; +} + +//------------------------------------------------------------ + +void TRubberFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + + assert(m_regionOutlineModifier); + ((RubberModifier *)m_regionOutlineModifier)->saveData(os); +} + +//------------------------------------------------------------ + +void TRubberFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + TSolidColorStyle::drawRegion(cf, true, boundary); +} + +void TRubberFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + m_regionOutlineModifier->modify(rdf.m_ro); + flash.setFillColor(getMainColor()); + rdf.drawRegionOutline(flash); + // If Valentina prefers the angled version use this + // rdf.drawRegionOutline(flash,false); +} + +//*************************************************************************** +// TPointShadowFillStyle implementation +//*************************************************************************** + +TPointShadowFillStyle::TPointShadowFillStyle( + const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection, + double density, + double shadowSize, + double pointSize) + : TSolidColorStyle(bgColor), m_shadowColor(shadowColor), m_shadowDirection(normalize(shadowDirection)), m_shadowSize(shadowSize), m_density(density), m_pointSize(pointSize) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TPointShadowFillStyle::clone() const +{ + return new TPointShadowFillStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TPointShadowFillStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TPointShadowFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TPointShadowFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TPointShadowFillStyle", "Angle"); + break; + case 1: + value = QCoreApplication::translate("TPointShadowFillStyle", "Density"); + break; + case 2: + value = QCoreApplication::translate("TPointShadowFillStyle", "Size"); + break; + case 3: + value = QCoreApplication::translate("TPointShadowFillStyle", "Point Size"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TPointShadowFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + min = 0.0; + max = 360.0; + break; + case 1: + min = 0.0; + max = 1.0; + break; + case 2: + min = 0.0; + max = 100.0; + break; + case 3: + min = 0.01; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TPointShadowFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + + double degree = 0.0; + + switch (index) { + case 0: + degree = asin(m_shadowDirection.y); + if (m_shadowDirection.x < 0) + degree = TConsts::pi - degree; + if (degree < 0) + degree += pi2; + return degree * TConsts::invOf_pi_180; + + case 1: + return m_density; + + case 2: + return m_shadowSize; + + case 3: + return m_pointSize; + } + + //never + return 0; +} + +//----------------------------------------------------------------------------- + +void TPointShadowFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + double degree = 0.0; + + switch (index) { + case 0: + degree = value * TConsts::pi_180; + m_shadowDirection.x = cos(degree); + m_shadowDirection.y = sin(degree); + + break; + + case 1: + m_density = value; + break; + + case 2: + m_shadowSize = value; + break; + + case 3: + m_pointSize = value; + break; + } +} + +//------------------------------------------------------------ +void TPointShadowFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_shadowDirection.x >> m_shadowDirection.y; + is >> m_density; + is >> m_shadowSize; + is >> m_pointSize; + is >> m_shadowColor; +} + +//------------------------------------------------------------ + +void TPointShadowFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_shadowDirection.x << m_shadowDirection.y; + os << m_density; + os << m_shadowSize; + os << m_pointSize; + os << m_shadowColor; +} + +//------------------------------------------------------------ + +TPixel32 TPointShadowFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_shadowColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ +void TPointShadowFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_shadowColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +double TPointShadowFillStyle::triangleArea(const TPointD &a, const TPointD &b, const TPointD &c) const +{ + double ab = tdistance(a, b); + double ac = tdistance(a, c); + double bc = tdistance(b, c); + double s = (ab + bc + ac) / 2.0; + return sqrt(s * (s - ab) * (s - ac) * (s - bc)); +} + +//------------------------------------------------------------ + +void TPointShadowFillStyle::shadowOnEdge_parallel(const TPointD &p0, const TPointD &p1, + const TPointD &p2, TRandom &rnd) const +{ + + if (p0 == p1 || p1 == p2) + return; + + TPointD diff = normalize(rotate90(p1 - p0)); + double len1 = diff * m_shadowDirection; + + diff = normalize(rotate90(p2 - p1)); + double len2 = diff * m_shadowDirection; + + if (len1 >= 0 && len2 >= 0 && (len1 + len2) > 0) { + TPointD la = p1 + m_shadowDirection * len1 * m_shadowSize; + TPointD lb = p2 + m_shadowDirection * len2 * m_shadowSize; + double t = triangleArea(p1, p2, lb) + triangleArea(p2, lb, la); + int nb = (int)(m_density * t); + for (int i = 0; i < nb; i++) { + double q = rnd.getUInt(1001) / 1000.0; + double r = rnd.getUInt(1001) / 1000.0; + r = r * r; + TPointD u = p1 + (p2 - p1) * q; + u = u + r * (len1 * (1.0 - q) + len2 * q) * m_shadowDirection * m_shadowSize; + tglColor(TPixel32(m_shadowColor.r, + m_shadowColor.g, + m_shadowColor.b, + (int)((1.0 - r) * (double)m_shadowColor.m))); + tglVertex(u); + } + } +} + +//------------------------------------------------------------ + +int TPointShadowFillStyle::shadowOnEdge_parallel(TFlash &flash, + const TPointD &p0, const TPointD &p1, + const TPointD &p2, TRandom &rnd, + const double radius, + const bool isDraw) const +{ + int nbDraw = 0; + + if (p0 == p1 || p1 == p2) + return 0; + + TPointD diff = normalize(rotate90(p1 - p0)); + double len1 = diff * m_shadowDirection; + len1 = tmax(0.0, len1); + + diff = normalize(rotate90(p2 - p1)); + double len2 = diff * m_shadowDirection; + len2 = tmax(0.0, len2); + + if ((len1 + len2) > 0) { + TPointD la = p1 + m_shadowDirection * len1 * m_shadowSize; + TPointD lb = p2 + m_shadowDirection * len2 * m_shadowSize; + double t = triangleArea(p1, p2, lb) + triangleArea(p2, lb, la); + int nb = (int)(m_density * t); + for (int i = 0; i < nb; i++) { + double q = rnd.getUInt(1001) / 1000.0; + double r = rnd.getUInt(1001) / 1000.0; + r = r * r; + TPointD u = p1 + (p2 - p1) * q; + u = u + r * (len1 * (1.0 - q) + len2 * q) * m_shadowDirection * m_shadowSize; + nbDraw++; + if (isDraw) { + flash.setFillColor(TPixel32(m_shadowColor.r, m_shadowColor.g, m_shadowColor.b, (int)((1.0 - r) * 255))); + flash.drawEllipse(u, radius, radius); + //flash.drawDot(u,radius); + } + } + } + return nbDraw; +} + +//------------------------------------------------------------ + +void TPointShadowFillStyle::deleteSameVerts(TRegionOutline::Boundary::iterator &rit, + vector &pv) const +{ + pv.clear(); + if (rit->size() <= 0) + return; + TRegionOutline::PointVector::iterator it_beg = rit->begin(); + TRegionOutline::PointVector::iterator it_end = rit->end(); + TRegionOutline::PointVector::iterator it = it_beg; + pv.push_back(*it); + it++; + for (; it != it_end; it++) { + if (tdistance(*it, pv.back()) > TConsts::epsilon) { + pv.push_back(*it); + } + } + + if (pv.size() > 2) { + if (tdistance(*(pv.begin()), pv.back()) <= TConsts::epsilon) + pv.pop_back(); + } +} + +//------------------------------------------------------------ + +void TPointShadowFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + GLfloat pointSizeSave; + glGetFloatv(GL_POINT_SIZE, &pointSizeSave); + GLfloat sizes[2]; + glGetFloatv(GL_POINT_SIZE_RANGE, sizes); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_POINT_SMOOTH); + //glPointSize((float)(sizes[0]+(sizes[1]-sizes[0])*m_pointSize*0.01)); + tglEnablePointSmooth((float)(sizes[0] + (sizes[1] - sizes[0]) * m_pointSize * 0.01)); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = boundary.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = boundary.m_exterior.end(); + + TPixel32 color; + if (cf) + color = (*(cf))(m_shadowColor); + else + color = m_shadowColor; + + TRandom rnd; + + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) { + vector pv; + deleteSameVerts(regions_it, pv); + if (pv.size() < 3) + continue; + vector::iterator it_beg = pv.begin(); + vector::iterator it_end = pv.end(); + vector::iterator it_last = it_end - 1; + vector::iterator it0, it1, it2; + glBegin(GL_POINTS); + for (it1 = it_beg; it1 != it_end; it1++) { + it0 = it1 == it_beg ? it_last : it1 - 1; + it2 = it1 == it_last ? it_beg : it1 + 1; + + shadowOnEdge_parallel(TPointD(it0->x, it0->y), TPointD(it1->x, it1->y), + TPointD(it2->x, it2->y), rnd); + } + glEnd(); + } + + glPointSize(pointSizeSave); + stenc->disableMask(); +} + +//------------------------------------------------------------ + +void TPointShadowFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + + TRegionOutline::Boundary::iterator regions_it; + TRegionOutline::Boundary::iterator regions_it_b = rdf.m_ro.m_exterior.begin(); + TRegionOutline::Boundary::iterator regions_it_e = rdf.m_ro.m_exterior.end(); + + TPixel32 color = m_shadowColor; + TRandom rnd; + rnd.reset(); + + double sizes[2] = {0.15, 10.0}; + double radius = (sizes[0] + (sizes[1] - sizes[0]) * m_pointSize * 0.01); + + int nbDraw = 0; + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) { + vector pv; + deleteSameVerts(regions_it, pv); + if (pv.size() < 3) + continue; + vector::iterator it_beg = pv.begin(); + vector::iterator it_end = pv.end(); + vector::iterator it_last = it_end - 1; + vector::iterator it0, it1, it2; + for (it1 = it_beg; it1 != it_end; it1++) { + it0 = it1 == it_beg ? it_last : it1 - 1; + it2 = it1 == it_last ? it_beg : it1 + 1; + nbDraw += shadowOnEdge_parallel(flash, TPointD(it0->x, it0->y), TPointD(it1->x, it1->y), + TPointD(it2->x, it2->y), rnd, radius, false); + } + } + + rnd.reset(); + flash.drawRegion(*r, nbDraw + 1); // +1 bbox + flash.setFillColor(getMainColor()); + flash.drawRectangle(rdf.m_ro.m_bbox); + + flash.setThickness(0.0); + for (regions_it = regions_it_b; regions_it != regions_it_e; ++regions_it) { + vector pv; + deleteSameVerts(regions_it, pv); + if (pv.size() < 3) + continue; + vector::iterator it_beg = pv.begin(); + vector::iterator it_end = pv.end(); + vector::iterator it_last = it_end - 1; + vector::iterator it0, it1, it2; + for (it1 = it_beg; it1 != it_end; it1++) { + it0 = it1 == it_beg ? it_last : it1 - 1; + it2 = it1 == it_last ? it_beg : it1 + 1; + shadowOnEdge_parallel(flash, TPointD(it0->x, it0->y), TPointD(it1->x, it1->y), + TPointD(it2->x, it2->y), rnd, radius, true); + } + } +} + +//*************************************************************************** +// TDottedFillStyle implementation +//*************************************************************************** + +TDottedFillStyle::TDottedFillStyle( + const TPixel32 &bgColor, + const TPixel32 &pointColor, + const double dotSize, + const double dotDist, + const bool isShifted) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_dotSize(dotSize), m_dotDist(dotDist), m_isShifted(isShifted) +{ +} + +//------------------------------------------------------------ + +TDottedFillStyle::TDottedFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32(0, 0, 200)), m_pointColor(color), m_dotSize(3.0), m_dotDist(15.0), m_isShifted(true) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TDottedFillStyle::clone() const +{ + return new TDottedFillStyle(*this); +} + +//------------------------------------------------------------ + +int TDottedFillStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TDottedFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TDottedFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + + return index == 0 ? QCoreApplication::translate("TDottedFillStyle", "Dot Size") : QCoreApplication::translate("TDottedFillStyle", "Dot Distance"); +} + +//----------------------------------------------------------------------------- + +void TDottedFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 0.001; + max = 30.0; + } else { + min = 2.0; + max = 100.0; + } +} + +//----------------------------------------------------------------------------- + +double TDottedFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + + if (!index) + return m_dotSize; + else + return m_dotDist; +} + +//----------------------------------------------------------------------------- + +void TDottedFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + + if (!index) { + m_dotSize = value; + } else { + m_dotDist = value; + } +} + +//------------------------------------------------------------ + +void TDottedFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_dotSize; + is >> m_dotDist; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TDottedFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_dotSize; + os << m_dotDist; + os << m_pointColor; +} + +//------------------------------------------------------------ + +TPixel32 TDottedFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TDottedFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TDottedFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + double LDotDist = tmax(m_dotDist, 0.1); + //double LDotSize=m_dotSize; + bool LIsShifted = m_isShifted; + const bool isTransparent = m_pointColor.m < 255; + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + if (isTransparent) { + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + } + + TPixel32 color; + if (cf) + color = (*(cf))(m_pointColor); + else + color = m_pointColor; + + tglColor(color); + + int i = 0; + for (double y = boundary.m_bbox.y0; y <= boundary.m_bbox.y1; y += LDotDist, ++i) { + double x = LIsShifted && (i % 2) == 1 ? boundary.m_bbox.x0 + LDotDist / 2.0 : boundary.m_bbox.x0; + for (; x <= boundary.m_bbox.x1; x += LDotDist) + tglDrawDisk(TPointD(x, y), m_dotSize); + // tglDrawCircle(TPointD(x,y),m_dotSize); + } + + if (isTransparent) { + //tglColor(TPixel32::White); + //glDisable(GL_BLEND); + } + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +int TDottedFillStyle::nbClip(const double LDotDist, const bool LIsShifted, + const TRectD &bbox) const +{ + int nbClipLayers = 1; // the bbox rectangle + int i = 0; + for (double y = bbox.y0; y <= bbox.y1; y += LDotDist, ++i) { + double x = LIsShifted && (i % 2) == 1 ? bbox.x0 + LDotDist / 2.0 : bbox.x0; + for (; x <= bbox.x1; x += LDotDist) + nbClipLayers++; + } + return nbClipLayers; +} + +//------------------------------------------------------------ + +void TDottedFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + double LDotDist = tmax(m_dotDist, 0.1); + double LDotSize = m_dotSize; + bool LIsShifted = m_isShifted; + TRectD bbox(r->getBBox()); + + flash.setFillColor(TPixel::Black); + flash.drawRegion(*r, true); + int nClip = nbClip(LDotDist, LIsShifted, bbox); + + flash.drawRegion(*r, nClip); + + flash.setFillColor(getMainColor()); + flash.drawRectangle(bbox); + flash.setFillColor(m_pointColor); + + int i = 0; + for (double y = bbox.y0; y <= bbox.y1; y += LDotDist, ++i) { + double x = LIsShifted && (i % 2) == 1 ? bbox.x0 + LDotDist / 2.0 : bbox.x0; + for (; x <= bbox.x1; x += LDotDist) + flash.drawEllipse(TPointD(x, y), LDotSize, LDotSize); + } +} + +//*************************************************************************** +// TCheckedFillStyle implementation +//*************************************************************************** + +TCheckedFillStyle::TCheckedFillStyle( + const TPixel32 &bgColor, const TPixel32 &pointColor, + const double HDist, const double HAngle, + const double VDist, const double VAngle, const double Thickness) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_HDist(HDist), m_HAngle(HAngle), m_VDist(VDist), m_VAngle(VAngle), m_Thickness(Thickness) +{ +} + +//------------------------------------------------------------ + +TCheckedFillStyle::TCheckedFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::Transparent), m_pointColor(color), m_HDist(15.0), m_HAngle(0.0), m_VDist(15.0), m_VAngle(0.0), m_Thickness(6.0) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TCheckedFillStyle::clone() const +{ + return new TCheckedFillStyle(*this); +} + +//------------------------------------------------------------ + +int TCheckedFillStyle::getParamCount() const +{ + return 5; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TCheckedFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TCheckedFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 5); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TCheckedFillStyle", "Horiz Dist"); + break; + case 1: + value = QCoreApplication::translate("TCheckedFillStyle", "Horiz Angle"); + break; + case 2: + value = QCoreApplication::translate("TCheckedFillStyle", "Vert Dist"); + break; + case 3: + value = QCoreApplication::translate("TCheckedFillStyle", "Vert Angle"); + break; + case 4: + value = QCoreApplication::translate("TCheckedFillStyle", "Thickness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TCheckedFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + min = 1.0; + max = 100.0; + break; + case 1: + min = -45.0; + max = 45.0; + break; + case 2: + min = 1.0; + max = 100.0; + break; + case 3: + min = -45.0; + max = 45.0; + break; + case 4: + min = 0.5; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TCheckedFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 5); + + switch (index) { + case 0: + return m_HDist; + case 1: + return m_HAngle; + case 2: + return m_VDist; + case 3: + return m_VAngle; + case 4: + return m_Thickness; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TCheckedFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 5); + + switch (index) { + case 0: + m_HDist = value; + break; + case 1: + m_HAngle = value; + break; + case 2: + m_VDist = value; + break; + case 3: + m_VAngle = value; + break; + case 4: + m_Thickness = value; + break; + } +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_HDist; + is >> m_HAngle; + is >> m_VDist; + is >> m_VAngle; + is >> m_Thickness; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_HDist; + os << m_HAngle; + os << m_VDist; + os << m_VAngle; + os << m_Thickness; + os << m_pointColor; +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::getHThickline(const TPointD &lc, const double lx, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const +{ + double l = m_Thickness / cos(degree2rad(m_HAngle)); + l *= 0.5; + p0 = TPointD(lc.x, lc.y - l); + p1 = TPointD(lc.x, lc.y + l); + double y = lc.y + lx * tan(degree2rad(m_HAngle)); + p2 = TPointD(lc.x + lx, y + l); + p3 = TPointD(lc.x + lx, y - l); +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::getVThickline(const TPointD &lc, const double ly, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const +{ + double l = m_Thickness / cos(degree2rad(-m_VAngle)); + l *= 0.5; + p0 = TPointD(lc.x - l, lc.y); + p1 = TPointD(lc.x + l, lc.y); + double x = lc.x + ly * tan(degree2rad(-m_VAngle)); + p2 = TPointD(x + l, lc.y + ly); + p3 = TPointD(x - l, lc.y + ly); +} + +//------------------------------------------------------------ + +TPixel32 TCheckedFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + const bool isTransparent = m_pointColor.m < 255; + if (isTransparent) { + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + } + + glBegin(GL_QUADS); + TPixel32 color; + if (cf) + color = (*(cf))(m_pointColor); + else + color = m_pointColor; + + tglColor(color); + + // Horizontal Lines + double lx = boundary.m_bbox.x1 - boundary.m_bbox.x0; + double ly = boundary.m_bbox.y1 - boundary.m_bbox.y0; + double beg = boundary.m_bbox.y0; + double end = boundary.m_bbox.y1; + beg = m_HAngle <= 0 ? beg : beg - lx * tan(degree2rad(m_HAngle)); + end = m_HAngle >= 0 ? end : end - lx * tan(degree2rad(m_HAngle)); + double dist = m_HDist / cos(degree2rad(m_HAngle)); + for (double y = beg; y <= end; y += dist) { + TPointD p0, p1, p2, p3; + getHThickline(TPointD(boundary.m_bbox.x0, y), lx, p0, p1, p2, p3); + tglVertex(p0); + tglVertex(p1); + tglVertex(p2); + tglVertex(p3); + } + + // Vertical Lines + beg = boundary.m_bbox.x0; + end = boundary.m_bbox.x1; + beg = (-m_VAngle) <= 0 ? beg : beg - ly * tan(degree2rad(-m_VAngle)); + end = (-m_VAngle) >= 0 ? end : end - ly * tan(degree2rad(-m_VAngle)); + dist = m_VDist / cos(degree2rad(-m_VAngle)); + for (double x = beg; x <= end; x += dist) { + TPointD p0, p1, p2, p3; + getVThickline(TPointD(x, boundary.m_bbox.y0), ly, p0, p1, p2, p3); + tglVertex(p0); + tglVertex(p1); + tglVertex(p2); + tglVertex(p3); + } + + glEnd(); + + if (isTransparent) { + //tglColor(TPixel32::White); + //glDisable(GL_BLEND); + } + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +int TCheckedFillStyle::nbClip(const TRectD &bbox) const +{ + int nbClip = 1; // the bbox rectangle + + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + double beg = bbox.y0; + double end = bbox.y1; + beg = m_HAngle <= 0 ? beg : beg - lx * tan(degree2rad(m_HAngle)); + end = m_HAngle >= 0 ? end : end - lx * tan(degree2rad(m_HAngle)); + double dist = m_HDist / cos(degree2rad(m_HAngle)); + for (double y = beg; y <= end; y += dist) + nbClip++; + + // Vertical lines + beg = bbox.x0; + end = bbox.x1; + beg = (-m_VAngle) <= 0 ? beg : beg - ly * tan(degree2rad(-m_VAngle)); + end = (-m_VAngle) >= 0 ? end : end - ly * tan(degree2rad(-m_VAngle)); + dist = m_VDist / cos(degree2rad(-m_VAngle)); + for (double x = beg; x <= end; x += dist) + nbClip++; + return nbClip; +} + +//------------------------------------------------------------ + +void TCheckedFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + + // flash.drawRegion(*r,true); + flash.drawRegion(*r, nbClip(bbox)); + + flash.setFillColor(getMainColor()); + flash.drawRectangle(bbox); + + flash.setFillColor(m_pointColor); + // Horizontal Lines + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + double beg = bbox.y0; + double end = bbox.y1; + beg = m_HAngle <= 0 ? beg : beg - lx * tan(degree2rad(m_HAngle)); + end = m_HAngle >= 0 ? end : end - lx * tan(degree2rad(m_HAngle)); + double dist = m_HDist / cos(degree2rad(m_HAngle)); + for (double y = beg; y <= end; y += dist) { + TPointD p0, p1, p2, p3; + getHThickline(TPointD(bbox.x0, y), lx, p0, p1, p2, p3); + vector v; + v.push_back(p0); + v.push_back(p1); + v.push_back(p2); + v.push_back(p3); + flash.drawPolyline(v); + } + + // Vertical lines + beg = bbox.x0; + end = bbox.x1; + beg = (-m_VAngle) <= 0 ? beg : beg - ly * tan(degree2rad(-m_VAngle)); + end = (-m_VAngle) >= 0 ? end : end - ly * tan(degree2rad(-m_VAngle)); + dist = m_VDist / cos(degree2rad(-m_VAngle)); + for (double x = beg; x <= end; x += dist) { + TPointD p0, p1, p2, p3; + getVThickline(TPointD(x, bbox.y0), ly, p0, p1, p2, p3); + vector v; + v.push_back(p0); + v.push_back(p1); + v.push_back(p2); + v.push_back(p3); + flash.drawPolyline(v); + } +} + +//*************************************************************************** +// ArtisticModifier implementation +//*************************************************************************** + +void ArtisticModifier::modify(TRegionOutline &outline) const +{ + + TRegionOutline::Boundary::iterator regIt = outline.m_exterior.begin(); + TRegionOutline::Boundary::iterator regItEnd = outline.m_exterior.end(); + + TRegionOutline::PointVector::iterator pIt; + TRegionOutline::PointVector::iterator pItEnd; + TRandom rnd; + double counter = 0; + double maxcounter = 0; + for (; regIt != regItEnd; ++regIt) { + pIt = regIt->begin(); + pItEnd = regIt->end(); + + for (; pIt != pItEnd; ++pIt) { + if (counter >= maxcounter) { + double tmp = (201 - m_period) * (rnd.getFloat() + 1); + maxcounter = tmp * tmp; + counter = 0; + } + if (pIt != regIt->begin()) { + double distance = (pIt->x - (pIt - 1)->x) * (pIt->x - (pIt - 1)->x) + (pIt->y - (pIt - 1)->y) * (pIt->y - (pIt - 1)->y); + counter += distance; + } + double wave = 1; + if (maxcounter) + wave = sin(pi2 * counter / maxcounter); + + pIt->x += m_move.x * wave; + pIt->y += m_move.y * wave; + } + } + + regIt = outline.m_interior.begin(); + regItEnd = outline.m_interior.end(); + for (; regIt != regItEnd; ++regIt) { + pIt = regIt->begin(); + pItEnd = regIt->end(); + + for (; pIt != pItEnd; ++pIt) { + pIt->x += (0.5 - rnd.getFloat()) * m_move.x; + pIt->y += (0.5 - rnd.getFloat()) * m_move.y; + } + } +} + +//------------------------------------------------------------ + +TOutlineStyle::RegionOutlineModifier *ArtisticModifier::clone() const +{ + return new ArtisticModifier(*this); +} + +//*************************************************************************** +// ArtisticSolidColor implementation +//*************************************************************************** + +ArtisticSolidColor::ArtisticSolidColor( + const TPixel32 &color, const TPointD &move, double period) + : TSolidColorStyle(color) +{ + m_regionOutlineModifier = new ArtisticModifier(move, period); +} + +//------------------------------------------------------------ + +TColorStyle *ArtisticSolidColor::clone() const +{ + return new ArtisticSolidColor(*this); +} + +//------------------------------------------------------------ + +void ArtisticSolidColor::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + delete m_regionOutlineModifier; + ArtisticModifier *mov = new ArtisticModifier(TPointD(), double()); + mov->loadData(is); + m_regionOutlineModifier = mov; +} + +//------------------------------------------------------------ + +void ArtisticSolidColor::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + + assert(m_regionOutlineModifier); + ((ArtisticModifier *)m_regionOutlineModifier)->saveData(os); +} + +//------------------------------------------------------------ + +int ArtisticSolidColor::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType ArtisticSolidColor::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString ArtisticSolidColor::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("ArtisticSolidColor", "Horiz Offset"); + break; + case 1: + value = QCoreApplication::translate("ArtisticSolidColor", "Vert Offset"); + break; + case 2: + value = QCoreApplication::translate("ArtisticSolidColor", "Noise"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void ArtisticSolidColor::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + min = 0.0; + max = 20.0; + break; + case 1: + min = 0.0; + max = 20.0; + break; + case 2: + min = 0.0; + max = 200.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double ArtisticSolidColor::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + double value; + switch (index) { + case 0: + value = ((ArtisticModifier *)m_regionOutlineModifier)->getMovePoint().x; + break; + case 1: + value = ((ArtisticModifier *)m_regionOutlineModifier)->getMovePoint().y; + break; + case 2: + value = ((ArtisticModifier *)m_regionOutlineModifier)->getPeriod(); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void ArtisticSolidColor::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + + TPointD oldMove = ((ArtisticModifier *)m_regionOutlineModifier)->getMovePoint(); + double oldPeriod = ((ArtisticModifier *)m_regionOutlineModifier)->getPeriod(); + + switch (index) { + case 0: + if (oldMove.x != value) { + delete m_regionOutlineModifier; + oldMove.x = value; + m_regionOutlineModifier = new ArtisticModifier(oldMove, oldPeriod); + updateVersionNumber(); + } + break; + case 1: + if (oldMove.y != value) { + delete m_regionOutlineModifier; + oldMove.y = value; + m_regionOutlineModifier = new ArtisticModifier(oldMove, oldPeriod); + updateVersionNumber(); + } + break; + case 2: + if (oldPeriod != value) { + delete m_regionOutlineModifier; + oldPeriod = value; + m_regionOutlineModifier = new ArtisticModifier(oldMove, oldPeriod); + updateVersionNumber(); + } + break; + } +} + +//------------------------------------------------------------ + +void ArtisticSolidColor::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + TSolidColorStyle::drawRegion(cf, true, boundary); +} + +//------------------------------------------------------------ + +void ArtisticSolidColor::drawRegion(TFlash &flash, const TRegion *r) const +{ + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + m_regionOutlineModifier->modify(rdf.m_ro); + flash.setFillColor(getMainColor()); + rdf.drawRegionOutline(flash, false); +} + +//*************************************************************************** +// TChalkFillStyle implementation +//*************************************************************************** + +TChalkFillStyle::TChalkFillStyle(const TPixel32 &color0, const TPixel32 &color1, + const double density, const double size) + : TSolidColorStyle(color1), m_color0(color0), m_density(density), m_size(size) +{ +} + +//------------------------------------------------------------ + +TChalkFillStyle::TChalkFillStyle(const TPixel32 &color0, const TPixel32 &color1) + : TSolidColorStyle(color0), m_color0(color1), m_density(25.0), m_size(1.0) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TChalkFillStyle::clone() const +{ + return new TChalkFillStyle(*this); +} + +//------------------------------------------------------------ + +int TChalkFillStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TChalkFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TChalkFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TChalkFillStyle", "Density"); + break; + case 1: + value = QCoreApplication::translate("TChalkFillStyle", "Dot Size"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TChalkFillStyle::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 1133) + throw TException("Chalk Fill style: unknown obsolete format"); + TSolidColorStyle::loadData(is); + is >> m_color0 >> m_density >> m_size; + m_density = m_density / 1000; + if (m_density > 100) + m_density = 100; +} + +//----------------------------------------------------------------------------- + +void TChalkFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + switch (index) { + case 0: + min = 0; + max = 100.0; + break; + case 1: + min = 0.0; + max = 10.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TChalkFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + double value; + switch (index) { + case 0: + value = m_density; + break; + case 1: + value = m_size; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TChalkFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + + switch (index) { + case 0: + m_density = value; + break; + case 1: + m_size = value; + break; + } +} + +//------------------------------------------------------------ + +void TChalkFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_color0; + is >> m_density; + is >> m_size; +} + +//------------------------------------------------------------ +void TChalkFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_color0; + os << m_density; + os << m_size; +} +//------------------------------------------------------------ +TPixel32 TChalkFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_color0 : TSolidColorStyle::getMainColor(); +} +//------------------------------------------------------------ +void TChalkFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_color0 = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TChalkFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + //const bool isTransparent=m_color0.m<255; + + //TRegionOutline::Boundary& exter=*(boundary.m_exterior); + //TRegionOutline::Boundary& inter=*(boundary.m_interior); + + TPixel32 color0; + if (cf) + color0 = (*(cf))(m_color0); + else + color0 = m_color0; + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + int chalkId = glGenLists(1); + glNewList(chalkId, GL_COMPILE); + glBegin(GL_QUADS); + glVertex2d(m_size, m_size); + glVertex2d(-m_size, m_size); + glVertex2d(-m_size, -m_size); + glVertex2d(m_size, -m_size); + glEnd(); + glEndList(); + TRandom rnd; + + double lx = boundary.m_bbox.x1 - boundary.m_bbox.x0; + double ly = boundary.m_bbox.y1 - boundary.m_bbox.y0; + + // cioe' imposta una densita' tale, per cui in una regione che ha bbox 200x200 + // inserisce esattamente m_density punti + int pointNumber = (int)(m_density * ((lx * ly) * 0.02)); + + for (int i = 0; i < pointNumber; i++) { + TPixel32 tmpcolor = color0; + double shiftx = boundary.m_bbox.x0 + rnd.getFloat() * lx; + double shifty = boundary.m_bbox.y0 + rnd.getFloat() * ly; + tmpcolor.m = (UCHAR)(tmpcolor.m * rnd.getFloat()); + tglColor(tmpcolor); + glPushMatrix(); + glTranslated(shiftx, shifty, 0.0); + glCallList(chalkId); + glPopMatrix(); + } + + //glEnd(); e questo che era??? + + glDeleteLists(chalkId, 1); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +void TChalkFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + + TPixel32 bgColor = TSolidColorStyle::getMainColor(); + + double minDensity; + double maxDensity; + + getParamRange(0, minDensity, maxDensity); + + double r1 = (m_density - minDensity) / (maxDensity - minDensity); + double r2 = 1.0 - r1; + + TPixel32 color((int)(bgColor.r * r2 + m_color0.r * r1), + (int)(bgColor.g * r2 + m_color0.g * r1), + (int)(bgColor.b * r2 + m_color0.b * r1), + (int)(bgColor.m * r2 + m_color0.m * r1)); + + flash.setFillColor(color); + flash.drawRegion(*r); + + /* + SFlashUtils rdf(r); + rdf.computeRegionOutline(); + + TRandom rnd; + + const bool isTransparent=m_color0.m<255; + + TRegionOutline::Boundary& exter=*(rdf.m_ro.m_exterior); + TRegionOutline::Boundary& inter=*(rdf.m_ro.m_interior); + + TPixel32 color0=m_color0; + + double lx=rdf.m_ro.m_bbox.x1-rdf.m_ro.m_bbox.x0; + double ly=rdf.m_ro.m_bbox.y1-rdf.m_ro.m_bbox.y0; + + // cioe' imposta una densita' tale, per cui in una regione che ha bbox 200x200 + // inserisce esattamente m_density punti + int pointNumber= (int)(m_density*((lx*ly)*0.000025)); + + flash.drawRegion(*r,pointNumber+1); // -1 i don't know why + + flash.setFillColor(getMainColor()); + flash.drawRectangle(TRectD(TPointD(rdf.m_ro.m_bbox.x0,rdf.m_ro.m_bbox.y0), + TPointD(rdf.m_ro.m_bbox.x1,rdf.m_ro.m_bbox.y1))); + + flash.setThickness(0.0); + for( int i=0;i< pointNumber; i++ ) { + TPixel32 tmpcolor=color0; + double shiftx=rdf.m_ro.m_bbox.x0+rnd.getFloat()*lx; + double shifty=rdf.m_ro.m_bbox.y0+rnd.getFloat()*ly; + tmpcolor.m=(UCHAR)(tmpcolor.m*rnd.getFloat()); + flash.setFillColor(tmpcolor); + flash.pushMatrix(); + TTranslation tM(shiftx, shifty); + flash.multMatrix(tM); + flash.drawRectangle(TRectD(TPointD(-1,-1),TPointD(1,1))); + flash.popMatrix(); + } + */ +} + +//*************************************************************************** +// TChessFillStyle implementation +//*************************************************************************** + +TChessFillStyle::TChessFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double HDist, const double VDist, const double Angle) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_HDist(HDist), m_VDist(VDist), m_Angle(Angle) +{ +} + +//------------------------------------------------------------ + +TChessFillStyle::TChessFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::White), m_pointColor(color), m_HDist(10.0), m_VDist(10.0), m_Angle(0.0) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TChessFillStyle::clone() const +{ + return new TChessFillStyle(*this); +} + +//------------------------------------------------------------ + +int TChessFillStyle::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TChessFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TChessFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TChessFillStyle", "Horiz Size"); + break; + case 1: + value = QCoreApplication::translate("TChessFillStyle", "Vert Size"); + break; + case 2: + value = QCoreApplication::translate("TChessFillStyle", "Angle"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TChessFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + min = 1.0; + max = 100.0; + break; + case 1: + min = 1.0; + max = 100.0; + break; + case 2: + min = -45.0; + max = 45.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TChessFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + return m_HDist; + case 1: + return m_VDist; + case 2: + return m_Angle; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TChessFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + m_HDist = value; + break; + case 1: + m_VDist = value; + break; + case 2: + m_Angle = value; + break; + } +} + +//------------------------------------------------------------ + +void TChessFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_HDist; + is >> m_VDist; + is >> m_Angle; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TChessFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_HDist; + os << m_VDist; + os << m_Angle; + os << m_pointColor; +} + +//------------------------------------------------------------ + +TPixel32 TChessFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TChessFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TChessFillStyle::makeGrid(TRectD &bbox, TRotation &rotM, vector &grid, + int &nbClip) const +{ + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + TPointD center = TPointD((bbox.x1 + bbox.x0) * 0.5, + (bbox.y1 + bbox.y0) * 0.5); + double l = (lx + ly) / 1.3; + double l2 = l / 2; + + bool isFirst = true; + for (double y = -l2; y < (l2 + m_VDist); y += m_VDist) { + double x = isFirst ? -l2 : -l2 + m_HDist; + isFirst = !isFirst; + for (; x < (l2 + m_HDist); x += 2 * m_HDist) { + grid.push_back(rotM * TPointD(x, y) + center); + nbClip++; + } + } +} + +//------------------------------------------------------------ + +void TChessFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + // const bool isTransparent=m_pointColor.m<255; + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + TPixel32 color; + if (cf) + color = (*(cf))(m_pointColor); + else + color = m_pointColor; + + tglColor(color); + + TPointD vert[4]; + vert[0].x = -0.5; + vert[0].y = 0.5; + vert[1].x = -0.5; + vert[1].y = -0.5; + vert[2].x = 0.5; + vert[2].y = -0.5; + vert[3].x = 0.5; + vert[3].y = 0.5; + + TRotation rotM(m_Angle); + TScale scaleM(m_HDist, m_VDist); + for (int i = 0; i < 4; i++) + vert[i] = rotM * scaleM * vert[i]; + + int chessId = glGenLists(1); + glNewList(chessId, GL_COMPILE); + glBegin(GL_QUADS); + glVertex2d(vert[0].x, vert[0].y); + glVertex2d(vert[1].x, vert[1].y); + glVertex2d(vert[2].x, vert[2].y); + glVertex2d(vert[3].x, vert[3].y); + glEnd(); + glEndList(); + + int nbClip = 1; + vector grid; + makeGrid(boundary.m_bbox, rotM, grid, nbClip); + + vector::const_iterator it = grid.begin(); + vector::const_iterator ite = grid.end(); + for (; it != ite; it++) { + glPushMatrix(); + glTranslated(it->x, it->y, 0.0); + glCallList(chessId); + glPopMatrix(); + } + + stenc->disableMask(); + glDeleteLists(chessId, 1); +} + +//------------------------------------------------------------ + +void TChessFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + + TPointD vert[4]; + vert[0].x = -0.5; + vert[0].y = 0.5; + vert[1].x = -0.5; + vert[1].y = -0.5; + vert[2].x = 0.5; + vert[2].y = -0.5; + vert[3].x = 0.5; + vert[3].y = 0.5; + + TRotation rotM(m_Angle); + TScale scaleM(m_HDist, m_VDist); + for (int i = 0; i < 4; i++) + vert[i] = rotM * scaleM * vert[i]; + + int nbClip = 1; // just for the getMainColor() rectangle + vector grid; + makeGrid(bbox, rotM, grid, nbClip); + + // flash.drawRegion(*r,true); + flash.drawRegion(*r, nbClip); + + flash.setFillColor(getMainColor()); + flash.drawRectangle(bbox); + + flash.setFillColor(m_pointColor); + + vector::const_iterator it = grid.begin(); + vector::const_iterator ite = grid.end(); + for (; it != ite; it++) { + TTranslation trM(it->x, it->y); + vector lvert; + lvert.push_back(trM * vert[0]); + lvert.push_back(trM * vert[1]); + lvert.push_back(trM * vert[2]); + lvert.push_back(trM * vert[3]); + flash.drawPolyline(lvert); + } +} + +//*************************************************************************** +// TStripeFillStyle implementation +//*************************************************************************** + +TStripeFillStyle::TStripeFillStyle( + const TPixel32 &bgColor, const TPixel32 &pointColor, + const double Dist, const double Angle, const double Thickness) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_Dist(Dist), m_Angle(Angle), m_Thickness(Thickness) +{ +} + +//------------------------------------------------------------ + +TStripeFillStyle::TStripeFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::Transparent), m_pointColor(color), m_Dist(15.0), m_Angle(0.0), m_Thickness(6.0) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TStripeFillStyle::clone() const +{ + return new TStripeFillStyle(*this); +} + +//------------------------------------------------------------ + +int TStripeFillStyle::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TStripeFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TStripeFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TStripeFillStyle", "Distance"); + break; + case 1: + value = QCoreApplication::translate("TStripeFillStyle", "Angle"); + break; + case 2: + value = QCoreApplication::translate("TStripeFillStyle", "Thickness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TStripeFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + min = 1.0; + max = 100.0; + break; + case 1: + min = -90.0; + max = 90.0; + break; + case 2: + min = 0.5; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TStripeFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + return m_Dist; + case 1: + return m_Angle; + case 2: + return m_Thickness; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TStripeFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + m_Dist = value; + break; + case 1: + m_Angle = value; + break; + case 2: + m_Thickness = value; + break; + } +} + +//------------------------------------------------------------ + +void TStripeFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_Dist; + is >> m_Angle; + is >> m_Thickness; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TStripeFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_Dist; + os << m_Angle; + os << m_Thickness; + os << m_pointColor; +} + +void TStripeFillStyle::getThickline(const TPointD &lc, const double lx, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const +{ + double l = m_Thickness / cos(degree2rad(m_Angle)); + l *= 0.5; + p0 = TPointD(lc.x, lc.y - l); + p1 = TPointD(lc.x, lc.y + l); + double y = lc.y + lx * tan(degree2rad(m_Angle)); + p2 = TPointD(lc.x + lx, y + l); + p3 = TPointD(lc.x + lx, y - l); +} + +//------------------------------------------------------------ + +TPixel32 TStripeFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ +void TStripeFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +inline void trim(TPointD &p0, TPointD &p1, double y0, double y1) +{ + if (p0.y < y0) { + //Trim the first extreme of the segment at y0 + double t = (y0 - p0.y) / (p1.y - p0.y); + p0.x = p0.x + t * (p1.x - p0.x); + p0.y = y0; + } else if (p0.y > y1) { + //The same, at y1 + double t = (y1 - p0.y) / (p1.y - p0.y); + p0.x = p0.x + t * (p1.x - p0.x); + p0.y = y1; + } + + //Same for p1 + if (p1.y < y0) { + double t = (y0 - p1.y) / (p0.y - p1.y); + p1.x = p1.x + t * (p0.x - p1.x); + p1.y = y0; + } else if (p1.y > y1) { + double t = (y1 - p1.y) / (p0.y - p1.y); + p1.x = p1.x + t * (p0.x - p1.x); + p1.y = y1; + } +} + +//------------------------------------------------------------ + +void TStripeFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + const bool isTransparent = m_pointColor.m < 255; + + TStencilControl *stenc = TStencilControl::instance(); + + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); + appStyle.drawRegion(0, false, boundary); + } else { + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + if (isTransparent) { + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_BLEND); + //// <-- tglEnableBlending(); + } + + TPixel32 color; + if (cf) + color = (*(cf))(m_pointColor); + else + color = m_pointColor; + + tglColor(color); + + // Horizontal Lines + if (fabs(m_Angle) != 90) { + double lx = boundary.m_bbox.x1 - boundary.m_bbox.x0; + //double ly=boundary.m_bbox.y1-boundary.m_bbox.y0; + double beg = boundary.m_bbox.y0; + double end = boundary.m_bbox.y1; + beg = m_Angle <= 0 ? beg : beg - lx * tan(degree2rad(m_Angle)); + end = m_Angle >= 0 ? end : end - lx * tan(degree2rad(m_Angle)); + double dist = m_Dist / cos(degree2rad(m_Angle)); + + double y; + + TStencilControl *stenc2 = TStencilControl::instance(); + stenc2->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + + glBegin(GL_QUADS); + for (y = beg; y <= end; y += dist) { + TPointD p0, p1, p2, p3; + getThickline(TPointD(boundary.m_bbox.x0, y), lx, p0, p1, p2, p3); + tglVertex(p0); + tglVertex(p1); + tglVertex(p2); + tglVertex(p3); + } + glEnd(); + stenc2->endMask(); + + stenc2->enableMask(TStencilControl::SHOW_OUTSIDE); + + if (m_Angle != 0) //ANTIALIASING + { + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_BLEND); + //glEnable(GL_LINE_SMOOTH); + + tglEnableLineSmooth(); + + //NOTE: Trimming the fat lines is necessary outside the (-60, 60) angles interval + //seemingly due to a bug in MAC-Leopard's openGL implementation... + + glBegin(GL_LINES); + for (y = beg; y <= end; y += dist) { + TPointD p0, p1, p2, p3; + getThickline(TPointD(boundary.m_bbox.x0, y), lx, p0, p1, p2, p3); + trim(p1, p2, boundary.m_bbox.y0, boundary.m_bbox.y1); + tglVertex(p1); + tglVertex(p2); + + trim(p0, p3, boundary.m_bbox.y0, boundary.m_bbox.y1); + tglVertex(p0); + tglVertex(p3); + } + glEnd(); + } + stenc2->disableMask(); + + } else { + double beg = boundary.m_bbox.x0; + double end = boundary.m_bbox.x1; + double y0 = boundary.m_bbox.y0; + double y1 = boundary.m_bbox.y1; + + glBegin(GL_QUADS); + for (double x = beg; x <= end; x += m_Dist) { + TPointD p0(x, y0); + TPointD p1(x + m_Thickness, y0); + TPointD p2(x, y1); + TPointD p3(x + m_Thickness, y1); + tglVertex(p0); + tglVertex(p1); + tglVertex(p3); + tglVertex(p2); + } + glEnd(); + } + + //tglColor(TPixel32::White); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +int TStripeFillStyle::nbClip(const TRectD &bbox) const +{ + int nbClip = 1; // the bbox rectangle + + if (fabs(m_Angle) != 90) { + double lx = bbox.x1 - bbox.x0; + //double ly=bbox.y1-bbox.y0; + double beg = bbox.y0; + double end = bbox.y1; + beg = m_Angle <= 0 ? beg : beg - lx * tan(degree2rad(m_Angle)); + end = m_Angle >= 0 ? end : end - lx * tan(degree2rad(m_Angle)); + double dist = m_Dist / cos(degree2rad(m_Angle)); + for (double y = beg; y <= end; y += dist) + nbClip++; + } else { + double beg = bbox.x0; + double end = bbox.x1; + //double y0=bbox.y0; + //double y1=bbox.y1; + for (double x = beg; x <= end; x += m_Dist) + nbClip++; + } + + return nbClip; +} + +//------------------------------------------------------------ + +void TStripeFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + + // flash.drawRegion(*r,true); + flash.drawRegion(*r, nbClip(bbox)); // -1 i don't know why + + flash.setFillColor(getMainColor()); + flash.drawRectangle(bbox); + + flash.setFillColor(m_pointColor); + // Horizontal Lines + if (fabs(m_Angle) != 90) { + double lx = bbox.x1 - bbox.x0; + //double ly=bbox.y1-bbox.y0; + double beg = bbox.y0; + double end = bbox.y1; + beg = m_Angle <= 0 ? beg : beg - lx * tan(degree2rad(m_Angle)); + end = m_Angle >= 0 ? end : end - lx * tan(degree2rad(m_Angle)); + double dist = m_Dist / cos(degree2rad(m_Angle)); + for (double y = beg; y <= end; y += dist) { + TPointD p0, p1, p2, p3; + getThickline(TPointD(bbox.x0, y), lx, p0, p1, p2, p3); + vector v; + v.push_back(p0); + v.push_back(p1); + v.push_back(p2); + v.push_back(p3); + flash.drawPolyline(v); + } + } else { + double beg = bbox.x0; + double end = bbox.x1; + double y0 = bbox.y0; + double y1 = bbox.y1; + for (double x = beg; x <= end; x += m_Dist) { + TPointD p0(x, y0); + TPointD p1(x + m_Thickness, y0); + TPointD p2(x, y1); + TPointD p3(x + m_Thickness, y1); + vector v; + v.push_back(p0); + v.push_back(p1); + v.push_back(p3); + v.push_back(p2); + flash.drawPolyline(v); + } + } +} + +//------------------------------------------------------------ + +void TStripeFillStyle::makeIcon(const TDimension &d) +{ + // Saves the values of member variables and sets the right icon values + double LDist = m_Dist; + double LAngle = m_Angle; + double LThickness = m_Thickness; + + m_Dist *= 1.33; + m_Thickness *= 1.66; + + TColorStyle::makeIcon(d); + + m_Dist = LDist; + m_Angle = LAngle; + m_Thickness = LThickness; +} + +//*************************************************************************** +// TLinGradFillStyle implementation +//*************************************************************************** + +TLinGradFillStyle::TLinGradFillStyle( + const TPixel32 &bgColor, const TPixel32 &pointColor, + const double Angle, const double XPos, const double YPos, + const double Size) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_Angle(Angle), m_XPos(XPos), m_YPos(YPos), m_Size(Size) +{ +} + +//----------------------------------------------------------------------------- + +TLinGradFillStyle::TLinGradFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::White), m_pointColor(color), m_Angle(0.0), m_XPos(0.0), m_YPos(0.0), m_Size(100.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TLinGradFillStyle::clone() const +{ + return new TLinGradFillStyle(*this); +} + +//------------------------------------------------------------ + +int TLinGradFillStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TLinGradFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TLinGradFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TLinGradFillStyle", "Angle"); + break; + case 1: + value = QCoreApplication::translate("TLinGradFillStyle", "X Position"); + break; + case 2: + value = QCoreApplication::translate("TLinGradFillStyle", "Y Position"); + break; + case 3: + value = QCoreApplication::translate("TLinGradFillStyle", "Smoothness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TLinGradFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = -180.0; + max = 180.0; + break; + case 1: + min = -100.0; + max = 100.0; + break; + case 2: + min = -100.0; + max = 100.0; + break; + case 3: + min = 1.0; + max = 500.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TLinGradFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + return m_Angle; + case 1: + return m_XPos; + case 2: + return m_YPos; + case 3: + return m_Size; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TLinGradFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + m_Angle = value; + break; + case 1: + m_XPos = value; + break; + case 2: + m_YPos = value; + break; + case 3: + m_Size = value; + break; + } +} + +//------------------------------------------------------------ + +void TLinGradFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_Angle; + is >> m_XPos; + is >> m_YPos; + is >> m_Size; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TLinGradFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_Angle; + os << m_XPos; + os << m_YPos; + os << m_Size; + os << m_pointColor; +} + +//------------------------------------------------------------ + +TPixel32 TLinGradFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TLinGradFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TLinGradFillStyle::getRects(const TRectD &bbox, + vector &r0, + vector &r1, + vector &r2) const +{ + r0.clear(); + r1.clear(); + r2.clear(); + + TPointD p0, p1, p2, p3; + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + TPointD center((bbox.x1 + bbox.x0) / 2.0, (bbox.y1 + bbox.y0) / 2.0); + center = center + TPointD(m_XPos * 0.01 * lx * 0.5, m_YPos * 0.01 * ly * 0.5); + double l = tdistance(TPointD(bbox.x0, bbox.y0), TPointD(bbox.x1, bbox.y1)); + + r0.push_back(TPointD(-m_Size - l, l)); + r0.push_back(TPointD(-m_Size - l, -l)); + r0.push_back(TPointD(-m_Size, -l)); + r0.push_back(TPointD(-m_Size, l)); + + r1.push_back(TPointD(-m_Size, l)); + r1.push_back(TPointD(-m_Size, -l)); + r1.push_back(TPointD(m_Size, -l)); + r1.push_back(TPointD(m_Size, l)); + + r2.push_back(TPointD(m_Size, l)); + r2.push_back(TPointD(m_Size, -l)); + r2.push_back(TPointD(m_Size + l, -l)); + r2.push_back(TPointD(m_Size + l, l)); + + TRotation rotM(m_Angle); + TTranslation traM(center); + TAffine M(traM * rotM); + + for (int i = 0; i < 4; i++) { + r0[i] = M * r0[i]; + r1[i] = M * r1[i]; + r2[i] = M * r2[i]; + } +} + +//------------------------------------------------------------ + +void TLinGradFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + //only to create stencil mask + TStencilControl *stenc = TStencilControl::instance(); + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + stenc->endMask(); + + //compute colors + TPixel32 color1, color2; + if (cf) { + color1 = (*(cf))(TSolidColorStyle::getMainColor()); + color2 = (*(cf))(m_pointColor); + } else { + color1 = TSolidColorStyle::getMainColor(); + color2 = m_pointColor; + } + + //compute points + TRectD bbox(boundary.m_bbox); + vector r0, r1, r2; + getRects(bbox, r0, r1, r2); + assert(r0.size() == 4); + assert(r1.size() == 4); + assert(r2.size() == 4); + + //draw + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + glBegin(GL_QUADS); + + tglColor(color2); + int i = 0; + for (; i < 4; tglVertex(r0[i++])) + ; + tglVertex(r1[0]); + tglVertex(r1[1]); + + tglColor(color1); + tglVertex(r1[2]); + tglVertex(r1[3]); + for (i = 0; i < 4; tglVertex(r2[i++])) + ; + + glEnd(); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +// It is the new version, which uses XPos, YPos, Smooth parameters. +// There is a gap between the flat and graded regions. This is the reason, +// why the old version (without XPos, YPos, Smooth parameters) is used. +void TLinGradFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + vector rect; + + TPointD center((bbox.x1 + bbox.x0) / 2.0, (bbox.y1 + bbox.y0) / 2.0); + center = center + TPointD(m_XPos * 0.01 * (bbox.x1 - bbox.x0) * 0.5, m_YPos * 0.01 * (bbox.y1 - bbox.y0) * 0.5); + double l = tdistance(TPointD(bbox.x0, bbox.y0), TPointD(bbox.x1, bbox.y1)); + + TAffine M(TTranslation(center) * TRotation(m_Angle)); + + rect.push_back(M * TPointD(-m_Size, l)); + rect.push_back(M * TPointD(-m_Size, -l)); + rect.push_back(M * TPointD(m_Size, -l)); + rect.push_back(M * TPointD(m_Size, l)); + + flash.setThickness(0.0); + + SFlashUtils sfu; + sfu.drawGradedRegion(flash, rect, m_pointColor, getMainColor(), *r); +} + +/* +// --- Old version --- +void TLinGradFillStyle::drawRegion(TFlash& flash, const TRegion* r) const +{ + flash.drawRegion(*r,1); + TRectD bbox(r->getBBox()); + TPointD p0,p1,p2,p3; + p0=TPointD(bbox.x0,bbox.y0); + p1=TPointD(bbox.x0,bbox.y1); + p2=TPointD(bbox.x1,bbox.y0); + p3=TPointD(bbox.x1,bbox.y1); + vector pv; + if ( fabs(m_Angle)!=90 ) { + double tga=tan(degree2rad(fabs(m_Angle))); + double lx=bbox.x1-bbox.x0; + double ly=bbox.y1-bbox.y0; + double ax=lx/(tga*tga+1); + double bx=lx-ax; + double mx=ax*tga; + double rlylx=ly/lx; + double ay=ax*rlylx; + double by=bx*rlylx; + double my=mx*rlylx; + if ( m_Angle<=0.0) { + p0=p0+TPointD(-my,by); + p1=p1+TPointD(bx,mx); + p2=p2+TPointD(-bx,-mx); + p3=p3+TPointD(my,-by); + } else { + p0=p0+TPointD(bx,-mx); + p1=p1+TPointD(-my,-by); + p2=p2+TPointD(my,by); + p3=p3+TPointD(-bx,mx); + } + pv.push_back(p0); + pv.push_back(p1); + pv.push_back(p3); + pv.push_back(p2); + } else { + if ( m_Angle==-90 ) { + pv.push_back(p1); + pv.push_back(p3); + pv.push_back(p2); + pv.push_back(p0); + } else { + pv.push_back(p0); + pv.push_back(p2); + pv.push_back(p3); + pv.push_back(p1); + } + } + SFlashUtils sfu; + sfu.drawGradedPolyline(flash,pv,m_pointColor,getMainColor()); +} +*/ + +//*************************************************************************** +// TRadGradFillStyle implementation +//*************************************************************************** + +TRadGradFillStyle::TRadGradFillStyle( + const TPixel32 &bgColor, const TPixel32 &pointColor, + const double XPos, const double YPos, + const double Radius, const double Smooth) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_XPos(XPos), m_YPos(YPos), m_Radius(Radius), m_Smooth(Smooth) +{ +} + +//----------------------------------------------------------------------------- + +TRadGradFillStyle::TRadGradFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::White), m_pointColor(color), m_XPos(0.0), m_YPos(0.0), m_Radius(50.0), m_Smooth(50) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TRadGradFillStyle::clone() const +{ + return new TRadGradFillStyle(*this); +} + +//------------------------------------------------------------ + +int TRadGradFillStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TRadGradFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TRadGradFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TRadGradFillStyle", "X Position"); + break; + case 1: + value = QCoreApplication::translate("TRadGradFillStyle", "Y Position"); + break; + case 2: + value = QCoreApplication::translate("TRadGradFillStyle", "Radius"); + break; + case 3: + value = QCoreApplication::translate("TRadGradFillStyle", "Smoothness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TRadGradFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = -100.0; + max = 100.0; + break; + case 1: + min = -100.0; + max = 100.0; + break; + case 2: + min = 0.01; + max = 100.0; + break; + case 3: + min = 0.01; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TRadGradFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + return m_XPos; + case 1: + return m_YPos; + case 2: + return m_Radius; + case 3: + return m_Smooth; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TRadGradFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + m_XPos = value; + break; + case 1: + m_YPos = value; + break; + case 2: + m_Radius = value; + break; + case 3: + m_Smooth = value; + break; + } +} + +//------------------------------------------------------------ + +void TRadGradFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_XPos; + is >> m_YPos; + is >> m_Radius; + is >> m_Smooth; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TRadGradFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_XPos; + os << m_YPos; + os << m_Radius; + os << m_Smooth; + os << m_pointColor; +} + +//------------------------------------------------------------ + +TPixel32 TRadGradFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TRadGradFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TRadGradFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + TStencilControl *stenc = TStencilControl::instance(); + //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + stenc->endMask(); + + TPixel32 backgroundColor, color; + if (cf) { + backgroundColor = (*(cf))(TSolidColorStyle::getMainColor()); + color = (*(cf))(m_pointColor); + } else { + backgroundColor = TSolidColorStyle::getMainColor(); + color = m_pointColor; + } + + TRectD bbox(boundary.m_bbox); + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + double r2 = (double)tmax(lx, ly) * 5.0; + double r1 = 0.5 * (double)tmax(lx, ly) * m_Radius * 0.01; + double r0 = r1 * (100.0 - m_Smooth) * 0.01; + + TPointD center((bbox.x1 + bbox.x0) / 2.0, (bbox.y1 + bbox.y0) / 2.0); + center = center + TPointD(m_XPos * 0.01 * lx * 0.5, m_YPos * 0.01 * ly * 0.5); + + const double dAngle = 5.0; + vector sincos; + for (double angle = 0.0; angle <= 360.0; angle += dAngle) + sincos.push_back(TPointD(sin(degree2rad(angle)), cos(degree2rad(angle)))); + + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + glBegin(GL_TRIANGLE_FAN); + tglColor(color); + tglVertex(center); + int i = 0; + for (; i < (int)sincos.size(); i++) + tglVertex(center + TPointD(r0 * sincos[i].x, r0 * sincos[i].y)); + glEnd(); + + if (fabs(r0 - r1) > TConsts::epsilon) { + glBegin(GL_QUAD_STRIP); + for (i = 0; i < (int)sincos.size(); i++) { + tglColor(color); + tglVertex(center + TPointD(r0 * sincos[i].x, r0 * sincos[i].y)); + tglColor(backgroundColor); + tglVertex(center + TPointD(r1 * sincos[i].x, r1 * sincos[i].y)); + } + glEnd(); + } + + tglColor(backgroundColor); + glBegin(GL_QUAD_STRIP); + for (i = 0; i < (int)sincos.size(); i++) { + tglVertex(center + TPointD(r1 * sincos[i].x, r1 * sincos[i].y)); + tglVertex(center + TPointD(r2 * sincos[i].x, r2 * sincos[i].y)); + } + glEnd(); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +void TRadGradFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + double r1 = 0.5 * (double)tmax(lx, ly) * m_Radius * 0.01; + if (m_Smooth < 50) + r1 *= (0.3 * ((100 - m_Smooth) / 50.0) + 0.7); + TPointD center((bbox.x1 + bbox.x0) / 2.0, (bbox.y1 + bbox.y0) / 2.0); + center = center + TPointD(m_XPos * 0.01 * lx * 0.5, m_YPos * 0.01 * ly * 0.5); + + flash.setThickness(0.0); + TPixel32 mc(getMainColor()); + flash.setGradientFill(false, m_pointColor, mc, m_Smooth); + const double flashGrad = 16384.0; // size of gradient square + TTranslation tM(center.x, center.y); + TScale sM(2.0 * r1 / (flashGrad), 2.0 * r1 / (flashGrad)); + flash.setFillStyleMatrix(tM * sM); + flash.drawRegion(*r); +} + +//*************************************************************************** +// TCircleStripeFillStyle implementation +//*************************************************************************** + +TCircleStripeFillStyle::TCircleStripeFillStyle( + const TPixel32 &bgColor, const TPixel32 &pointColor, + const double XPos, const double YPos, + const double Dist, const double Thickness) + : TSolidColorStyle(bgColor), m_pointColor(pointColor), m_XPos(XPos), m_YPos(YPos), m_Dist(Dist), m_Thickness(Thickness) +{ +} + +//------------------------------------------------------------ + +TCircleStripeFillStyle::TCircleStripeFillStyle(const TPixel32 &color) + : TSolidColorStyle(TPixel32::Transparent), m_pointColor(color), m_XPos(0.0), m_YPos(0.0), m_Dist(15.0), m_Thickness(3.0) +{ +} + +//------------------------------------------------------------ + +TColorStyle *TCircleStripeFillStyle::clone() const +{ + return new TCircleStripeFillStyle(*this); +} + +//------------------------------------------------------------ + +int TCircleStripeFillStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TCircleStripeFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TCircleStripeFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TCircleStripeFillStyle", "X Position"); + break; + case 1: + value = QCoreApplication::translate("TCircleStripeFillStyle", "Y Position"); + break; + case 2: + value = QCoreApplication::translate("TCircleStripeFillStyle", "Distance"); + break; + case 3: + value = QCoreApplication::translate("TCircleStripeFillStyle", "Thickness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TCircleStripeFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = -200.0; + max = 200.0; + break; + case 1: + min = -200.0; + max = 200.0; + break; + case 2: + min = 0.5; + max = 100.0; + break; + case 3: + min = 0.5; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TCircleStripeFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + return m_XPos; + case 1: + return m_YPos; + case 2: + return m_Dist; + case 3: + return m_Thickness; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void TCircleStripeFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + m_XPos = value; + break; + case 1: + m_YPos = value; + break; + case 2: + m_Dist = value; + break; + case 3: + m_Thickness = value; + break; + } +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_XPos; + is >> m_YPos; + is >> m_Dist; + is >> m_Thickness; + is >> m_pointColor; +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_XPos; + os << m_YPos; + os << m_Dist; + os << m_Thickness; + os << m_pointColor; +} + +//------------------------------------------------------------ + +TPixel32 TCircleStripeFillStyle::getColorParamValue(int index) const +{ + return index == 0 ? m_pointColor : TSolidColorStyle::getMainColor(); +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + m_pointColor = color; + else { + TSolidColorStyle::setMainColor(color); + } +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::getCircleStripeQuads(const TPointD ¢er, + const double r1, const double r2, + vector &pv) const +{ + pv.clear(); + const double dAng = 10.0; + for (double ang = 0.0; ang <= 360; ang += dAng) { + pv.push_back(center + TPointD(r1 * cos(degree2rad(ang)), r1 * sin(degree2rad(ang)))); + pv.push_back(center + TPointD(r2 * cos(degree2rad(ang)), r2 * sin(degree2rad(ang)))); + } +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::drawCircleStripe(const TPointD ¢er, + const double r1, const double r2, + const TPixel32 &col) const +{ + vector pv; + getCircleStripeQuads(center, r1, r2, pv); + + TStencilControl *stencil = TStencilControl::instance(); + stencil->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + + glBegin(GL_QUAD_STRIP); + tglColor(col); + int i = 0; + for (; i < (int)pv.size(); i++) + tglVertex(pv[i]); + glEnd(); + + stencil->endMask(); + stencil->enableMask(TStencilControl::SHOW_OUTSIDE); + + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_BLEND); + //glEnable(GL_LINE_SMOOTH); + tglEnableLineSmooth(); + + // Just for the antialiasing + glBegin(GL_LINE_STRIP); + tglColor(col); + for (i = 0; i < (int)pv.size(); i += 2) + tglVertex(pv[i]); + glEnd(); + + glBegin(GL_LINE_STRIP); + tglColor(col); + for (i = 1; i < (int)pv.size(); i += 2) + tglVertex(pv[i]); + glEnd(); + + stencil->disableMask(); +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + const bool isTransparent = m_pointColor.m < 255; + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(m_pointColor); + + TPixel32 foregroundColor; + if (cf) + foregroundColor = (*(cf))(m_pointColor); + else + foregroundColor = m_pointColor; + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + if (isTransparent) { + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + } + + TRectD bbox = boundary.m_bbox; + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + TPointD center((bbox.x1 + bbox.x0) * 0.5, (bbox.y1 + bbox.y0) * 0.5); + center.x = center.x + m_XPos * 0.01 * 0.5 * lx; + center.y = center.y + m_YPos * 0.01 * 0.5 * ly; + + double maxDist = 0.0; + maxDist = tmax(tdistance(center, TPointD(bbox.x0, bbox.y0)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x0, bbox.y1)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x1, bbox.y0)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x1, bbox.y1)), maxDist); + + double halfThick = m_Thickness * 0.5; + for (double d = 0; d <= maxDist; d += m_Dist) + drawCircleStripe(center, d - halfThick, d + halfThick, foregroundColor); + + if (isTransparent) { + //tglColor(TPixel32::White); + //glDisable(GL_BLEND); + } + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +void TCircleStripeFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + TRectD bbox(r->getBBox()); + + double lx = bbox.x1 - bbox.x0; + double ly = bbox.y1 - bbox.y0; + TPointD center((bbox.x1 + bbox.x0) * 0.5, (bbox.y1 + bbox.y0) * 0.5); + center.x = center.x + m_XPos * 0.01 * 0.5 * lx; + center.y = center.y + m_YPos * 0.01 * 0.5 * ly; + + double maxDist = 0.0; + maxDist = tmax(tdistance(center, TPointD(bbox.x0, bbox.y0)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x0, bbox.y1)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x1, bbox.y0)), maxDist); + maxDist = tmax(tdistance(center, TPointD(bbox.x1, bbox.y1)), maxDist); + + int nbClip = 2; + double d = m_Dist; + for (; d <= maxDist; d += m_Dist) + nbClip++; + flash.setFillColor(TPixel::Black); + flash.drawRegion(*r, nbClip); + + flash.setFillColor(getMainColor()); + flash.drawRectangle(bbox); + + flash.setFillColor(m_pointColor); + flash.setLineColor(m_pointColor); + flash.setThickness(0.0); + d = m_Thickness / 2.0; + flash.drawEllipse(center, d, d); + + flash.setFillColor(TPixel32(0, 0, 0, 0)); + flash.setLineColor(m_pointColor); + flash.setThickness(m_Thickness / 2.0); + for (d = m_Dist; d <= maxDist; d += m_Dist) + flash.drawEllipse(center, d, d); +} + +//*************************************************************************** +// TMosaicFillStyle implementation +//*************************************************************************** + +TMosaicFillStyle::TMosaicFillStyle(const TPixel32 &bgColor, + const TPixel32 pointColor[4], + const double size, + const double deform, + const double minThickness, + const double maxThickness) + : TSolidColorStyle(bgColor), m_size(size), m_deform(deform), m_minThickness(minThickness), m_maxThickness(maxThickness) +{ + for (int i = 0; i < 5; i++) + m_pointColor[i] = pointColor[i]; +} + +//------------------------------------------------------------ + +TMosaicFillStyle::TMosaicFillStyle(const TPixel32 bgColor) + : TSolidColorStyle(bgColor), m_size(25.0), m_deform(70.0), m_minThickness(20), m_maxThickness(40) +{ + m_pointColor[0] = TPixel32::Blue; + m_pointColor[1] = TPixel32::Green; + m_pointColor[2] = TPixel32::Yellow; + m_pointColor[3] = TPixel32::Cyan; +} + +//------------------------------------------------------------ + +TColorStyle *TMosaicFillStyle::clone() const +{ + return new TMosaicFillStyle(*this); +} + +//------------------------------------------------------------ + +int TMosaicFillStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TMosaicFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TMosaicFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TMosaicFillStyle", "Size"); + break; + case 1: + value = QCoreApplication::translate("TMosaicFillStyle", "Distortion"); + break; + case 2: + value = QCoreApplication::translate("TMosaicFillStyle", "Min Thick"); + break; + case 3: + value = QCoreApplication::translate("TMosaicFillStyle", "Max Thick"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TMosaicFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + min = (index == 0) ? 2 : 0.001; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TMosaicFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + + double value; + switch (index) { + case 0: + value = m_size; + break; + case 1: + value = m_deform; + break; + case 2: + value = m_minThickness; + break; + case 3: + value = m_maxThickness; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TMosaicFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + switch (index) { + case 0: + m_size = value; + break; + case 1: + m_deform = value; + break; + case 2: + m_minThickness = value; + break; + case 3: + m_maxThickness = value; + break; + } +} + +//------------------------------------------------------------ + +void TMosaicFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_size; + is >> m_deform; + is >> m_minThickness; + is >> m_maxThickness; + is >> m_pointColor[0]; + is >> m_pointColor[1]; + is >> m_pointColor[2]; + is >> m_pointColor[3]; +} + +//------------------------------------------------------------ + +void TMosaicFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_size; + os << m_deform; + os << m_minThickness; + os << m_maxThickness; + os << m_pointColor[0]; + os << m_pointColor[1]; + os << m_pointColor[2]; + os << m_pointColor[3]; +} + +//------------------------------------------------------------ + +TPixel32 TMosaicFillStyle::getColorParamValue(int index) const +{ + TPixel32 tmp; + if (index == 0) + tmp = TSolidColorStyle::getMainColor(); + else if (index >= 1 && index <= 4) + tmp = m_pointColor[index - 1]; + else + assert(!"bad color index"); + + return tmp; +} + +//------------------------------------------------------------ +void TMosaicFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + TSolidColorStyle::setMainColor(color); + else if (index >= 1 && index <= 4) + m_pointColor[index - 1] = color; + else + assert(!"bad color index"); +} + +//------------------------------------------------------------ + +void TMosaicFillStyle::preaprePos(const TRectD &box, vector &v, + int &lX, int &lY, TRandom &rand) const +{ + double dist = 5.0 + (60.0 - 5.0) * tcrop(m_size, 0.0, 100.0) * 0.01; + lY = lX = 0; + double ld = 0.4 * tcrop(m_deform, 0.0, 100.0) * 0.01; + for (double y = box.y0 - dist; y <= (box.y1 + dist); y += dist, lY++) { + lX = 0; + for (double x = box.x0 - dist; x <= (box.x1 + dist); x += dist, lX++) { + double dx = (rand.getInt(0, 2001) * 0.001 - 1.0) * ld * dist; + double dy = (rand.getInt(0, 2001) * 0.001 - 1.0) * ld * dist; + TPointD pos(x + dx, y + dy); + v.push_back(pos); + } + } +} + +//------------------------------------------------------------ + +bool TMosaicFillStyle::getQuad(const int ix, const int iy, + const int lX, const int lY, + vector &v, + TPointD *pquad, TRandom &rand) const +{ + if (ix < 0 || iy < 0 || ix >= (lX - 1) || iy >= (lY - 1)) + return false; + + double dmin = tcrop(m_minThickness, 0.0, 100.0) * 0.01; + double dmax = tcrop(m_maxThickness, 0.0, 100.0) * 0.01; + + TPointD &p1 = v[iy * lX + ix]; + TPointD &p2 = v[iy * lX + ix + 1]; + TPointD &p3 = v[(iy + 1) * lX + ix + 1]; + TPointD &p4 = v[(iy + 1) * lX + ix]; + + double q1 = 0.5 * (dmin + (dmax - dmin) * rand.getInt(0, 101) * 0.01); + double q2 = 0.5 * (dmin + (dmax - dmin) * rand.getInt(0, 101) * 0.01); + double q3 = 0.5 * (dmin + (dmax - dmin) * rand.getInt(0, 101) * 0.01); + double q4 = 0.5 * (dmin + (dmax - dmin) * rand.getInt(0, 101) * 0.01); + + pquad[0] = (1.0 - q1) * p1 + q1 * p3; + pquad[1] = (1.0 - q2) * p2 + q2 * p4; + pquad[2] = (1.0 - q3) * p3 + q3 * p1; + pquad[3] = (1.0 - q4) * p4 + q4 * p2; + + return true; +} + +//------------------------------------------------------------ + +void TMosaicFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + + TPixel32 color[4]; + for (int i = 0; i < 4; i++) { + if (cf) + color[i] = (*(cf))(m_pointColor[i]); + else + color[i] = m_pointColor[i]; + } + TPixel32 currentColor; + + vector pos; + int posLX, posLY; + TRandom rand; + TPointD quad[4]; + + preaprePos(boundary.m_bbox, pos, posLX, posLY, rand); + + glBegin(GL_QUADS); + + /* ma serve ? + tglColor(getMainColor()); + tglVertex(TPointD(boundary.m_bbox.x0,boundary.m_bbox.y0)); + tglVertex(TPointD(boundary.m_bbox.x0,boundary.m_bbox.y1)); + tglVertex(TPointD(boundary.m_bbox.x1,boundary.m_bbox.y1)); + tglVertex(TPointD(boundary.m_bbox.x1,boundary.m_bbox.y0)); + */ + + for (int y = 0; y < (posLY - 1); y++) + for (int x = 0; x < (posLX - 1); x++) + if (getQuad(x, y, posLX, posLY, pos, quad, rand)) { + currentColor = color[rand.getInt(0, 4)]; + if (currentColor.m != 0) { + tglColor(currentColor); + tglVertex(quad[0]); + tglVertex(quad[1]); + tglVertex(quad[2]); + tglVertex(quad[3]); + } + } + glEnd(); + + //tglColor(TPixel32::White); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +void TMosaicFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + + TRectD bbox(r->getBBox()); + + vector pos; + int posLX, posLY; + TRandom rand; + TPointD quad[4]; + + preaprePos(bbox, pos, posLX, posLY, rand); + + if (pos.size() <= 0) + return; + + int nbClip = (posLX - 1) * (posLY - 1) + 1; + flash.drawRegion(*r, nbClip); + + flash.setFillColor(TSolidColorStyle::getMainColor()); + flash.setThickness(0); + flash.drawRectangle(bbox); + for (int y = 0; y < (posLY - 1); y++) + for (int x = 0; x < (posLX - 1); x++) + if (getQuad(x, y, posLX, posLY, pos, quad, rand)) { + vector lvert; + lvert.push_back(quad[0]); + lvert.push_back(quad[1]); + lvert.push_back(quad[2]); + lvert.push_back(quad[3]); + flash.setFillColor(m_pointColor[rand.getInt(0, 4)]); + flash.drawPolyline(lvert); + } +} + +//*************************************************************************** +// TPatchFillStyle implementation +//*************************************************************************** + +TPatchFillStyle::TPatchFillStyle(const TPixel32 &bgColor, + const TPixel32 pointColor[6], + const double size, + const double deform, + const double thickness) + : TSolidColorStyle(bgColor), m_size(size), m_deform(deform), m_thickness(thickness) +{ + for (int i = 0; i < 6; i++) + m_pointColor[i] = pointColor[i]; +} + +//----------------------------------------------------------------------------- + +TPatchFillStyle::TPatchFillStyle(const TPixel32 &bgColor) + : TSolidColorStyle(bgColor), m_size(25.0), m_deform(50.0), m_thickness(30) +{ + m_pointColor[0] = TPixel32::Red; + m_pointColor[1] = TPixel32::Green; + m_pointColor[2] = TPixel32::Yellow; + m_pointColor[3] = TPixel32::Cyan; + m_pointColor[4] = TPixel32::Magenta; + m_pointColor[5] = TPixel32::White; +} + +//----------------------------------------------------------------------------- + +TColorStyle *TPatchFillStyle::clone() const +{ + return new TPatchFillStyle(*this); +} + +//------------------------------------------------------------ + +int TPatchFillStyle::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TPatchFillStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TPatchFillStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TPatchFillStyle", "Size"); + break; + case 1: + value = QCoreApplication::translate("TPatchFillStyle", "Distortion"); + break; + case 2: + value = QCoreApplication::translate("TPatchFillStyle", "Thickness"); + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +void TPatchFillStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + min = (index == 0) ? 2 : 0.001; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TPatchFillStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + + double value; + switch (index) { + case 0: + value = m_size; + break; + case 1: + value = m_deform; + break; + case 2: + value = m_thickness; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TPatchFillStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + + switch (index) { + case 0: + m_size = value; + break; + case 1: + m_deform = value; + break; + case 2: + m_thickness = value; + break; + } +} + +//------------------------------------------------------------ + +void TPatchFillStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_size; + is >> m_deform; + is >> m_thickness; + for (int i = 0; i < 6; i++) + is >> m_pointColor[i]; +} + +//------------------------------------------------------------ + +void TPatchFillStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_size; + os << m_deform; + os << m_thickness; + for (int i = 0; i < 6; i++) + os << m_pointColor[i]; +} + +//------------------------------------------------------------ +TPixel32 TPatchFillStyle::getColorParamValue(int index) const +{ + TPixel32 tmp; + if (index == 0) + tmp = TSolidColorStyle::getMainColor(); + else if (index >= 1 && index <= 6) + tmp = m_pointColor[index - 1]; + else + assert(!"bad color index"); + + return tmp; +} + +//------------------------------------------------------------ +void TPatchFillStyle::setColorParamValue(int index, const TPixel32 &color) +{ + if (index == 0) + TSolidColorStyle::setMainColor(color); + else if (index >= 1 && index <= 6) + m_pointColor[index - 1] = color; + else + assert(!"bad color index"); +} + +//------------------------------------------------------------ + +void TPatchFillStyle::preaprePos(const TRectD &box, vector &v, + int &lX, int &lY, TRandom &rand) const +{ + double q = tcrop(m_size, 0.0, 100.0) * 0.01; + double r = 5.0 + (60.0 - 5.0) * q; + double m = r * sqrt(3.0) / 2.0; + lY = 5 + (int)((box.y1 - box.y0) / (2 * m)); + int ix = 0; + for (double x = box.x0 - r; x <= (box.x1 + r); ix++) { + int nb = ix % 4; + double y = (nb == 0 || nb == 1) ? box.y0 - 2 * m : box.y0 - m; + for (int iy = 0; iy < lY; iy++, y += (2 * m)) + v.push_back(TPointD(x, y)); + x = (nb == 0 || nb == 2) ? x + r : x + r / 2.0; + } + lX = ix; + + double maxDeform = r * 0.6 * tcrop(m_deform, 0.0, 100.0) * 0.01; + for (UINT i = 0; i < v.size(); i++) { + v[i].x += (rand.getInt(0, 200) - 100) * 0.01 * maxDeform; + v[i].y += (rand.getInt(0, 200) - 100) * 0.01 * maxDeform; + } +} + +//------------------------------------------------------------ + +bool TPatchFillStyle::getQuadLine(const TPointD &a, const TPointD &b, + const double thickn, TPointD *quad) const +{ + if (tdistance(a, b) < TConsts::epsilon) + return false; + + TPointD ab(b - a); + ab = normalize(ab); + ab = rotate90(ab); + ab = ab * thickn; + + quad[0] = a + ab; + quad[1] = a - ab; + quad[2] = b - ab; + quad[3] = b + ab; + + return true; +} + +//------------------------------------------------------------ + +void TPatchFillStyle::drawGLQuad(const TPointD *quad) const +{ + glBegin(GL_QUADS); + tglVertex(quad[0]); + tglVertex(quad[1]); + tglVertex(quad[2]); + tglVertex(quad[3]); + glEnd(); + double r = tdistance(quad[0], quad[1]) / 2.0; + tglDrawDisk(quad[0] * 0.5 + quad[1] * 0.5, r); + tglDrawDisk(quad[2] * 0.5 + quad[3] * 0.5, r); +} + +//------------------------------------------------------------ + +void TPatchFillStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ + + TStencilControl *stenc = TStencilControl::instance(); + TPixel32 backgroundColor = TSolidColorStyle::getMainColor(); + if (cf) + backgroundColor = (*(cf))(backgroundColor); + + if (backgroundColor.m == 0) { //only to create stencil mask + TSolidColorStyle appStyle(TPixel32::White); + stenc->beginMask(); //does not draw on screen + appStyle.drawRegion(0, false, boundary); + } else { //create stencil mask and draw on screen + stenc->beginMask(TStencilControl::DRAW_ALSO_ON_SCREEN); + TSolidColorStyle::drawRegion(cf, antiAliasing, boundary); + } + stenc->endMask(); + stenc->enableMask(TStencilControl::SHOW_INSIDE); + + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //// <-- tglEnableBlending(); + + TPixel32 color[6]; + for (int i = 0; i < 6; i++) + if (cf) + color[i] = (*(cf))(m_pointColor[i]); + else + color[i] = m_pointColor[i]; + + TPixel32 currentColor; + + vector pos; + int posLX, posLY; + TRandom rand; + TPointD quad[4]; + + preaprePos(boundary.m_bbox, pos, posLX, posLY, rand); + glBegin(GL_TRIANGLES); + int x = 2; + for (; x < (posLX - 2); x += 2) + for (int y = 1; y < posLY; y++) { + TPointD q[6]; + if ((x % 4) == 2) { + q[0] = pos[(x - 1) * posLY + y]; + q[1] = pos[(x)*posLY + y]; + q[2] = pos[(x + 1) * posLY + y]; + q[3] = pos[(x + 2) * posLY + y]; + q[4] = pos[(x + 1) * posLY + y - 1]; + q[5] = pos[(x)*posLY + y - 1]; + } else { + q[0] = pos[(x - 1) * posLY + y - 1]; + q[1] = pos[(x)*posLY + y - 1]; + q[2] = pos[(x + 1) * posLY + y - 1]; + q[3] = pos[(x + 2) * posLY + y - 1]; + q[4] = pos[(x + 1) * posLY + y]; + q[5] = pos[(x)*posLY + y]; + } + + currentColor = color[rand.getInt(0, 6)]; + if (currentColor.m != 0) { + tglColor(currentColor); + + tglVertex(q[0]); + tglVertex(q[1]); + tglVertex(q[2]); + + tglVertex(q[2]); + tglVertex(q[3]); + tglVertex(q[4]); + + tglVertex(q[4]); + tglVertex(q[5]); + tglVertex(q[0]); + + tglVertex(q[0]); + tglVertex(q[2]); + tglVertex(q[4]); + } + } + glEnd(); + + double thickn = tcrop(m_thickness, 0.0, 100.0) * 0.01 * 5.0; + if (thickn > 0.001) + tglColor(backgroundColor); + for (x = 0; x < (posLX - 1); x++) { + int nb = x % 4; + for (int y = 0; y < posLY; y++) { + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y], thickn, quad)) + drawGLQuad(quad); + if (y > 0 && nb == 1) + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y - 1], thickn, quad)) + drawGLQuad(quad); + if (y < (posLY - 1) && nb == 3) + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y + 1], thickn, quad)) + drawGLQuad(quad); + } + } + + //tglColor(TPixel32::White); + + stenc->disableMask(); +} + +//------------------------------------------------------------ + +int TPatchFillStyle::nbClip(const int lX, const int lY, const vector &v) const +{ + TPointD quad[4]; + double thickn = tcrop(m_thickness, 0.0, 100.0) * 0.01 * 5.0; + int nbC = 0; + int x = 2; + for (; x < (lX - 2); x += 2) + for (int y = 1; y < lY; y++) + nbC += 1; + if (thickn > 0.001) + for (x = 0; x < (lX - 1); x++) { + int nb = x % 4; + for (int y = 0; y < lY; y++) { + if (getQuadLine(v[x * lY + y], v[(x + 1) * lY + y], thickn, quad)) + nbC += 3; + if (y > 0 && nb == 1) + if (getQuadLine(v[x * lY + y], v[(x + 1) * lY + y - 1], thickn, quad)) + nbC += 3; + if (y < (lY - 1) && nb == 3) + if (getQuadLine(v[x * lY + y], v[(x + 1) * lY + y + 1], thickn, quad)) + nbC += 3; + } + } + return nbC; +} + +//------------------------------------------------------------ + +void TPatchFillStyle::drawFlashQuad(TFlash &flash, const TPointD *quad) const +{ + vector lvert; + lvert.push_back(quad[0]); + lvert.push_back(quad[1]); + lvert.push_back(quad[2]); + lvert.push_back(quad[3]); + flash.drawPolyline(lvert); + + double r = tdistance(quad[0], quad[1]) / 2.0; + flash.drawEllipse(quad[0] * 0.5 + quad[1] * 0.5, r, r); + flash.drawEllipse(quad[2] * 0.5 + quad[3] * 0.5, r, r); +} + +//------------------------------------------------------------ + +void TPatchFillStyle::drawFlashTriangle(TFlash &flash, + const TPointD &p1, + const TPointD &p2, + const TPointD &p3) const +{ + vector lvert; + lvert.push_back(p1); + lvert.push_back(p2); + lvert.push_back(p3); + flash.drawPolyline(lvert); +} + +//------------------------------------------------------------ + +void TPatchFillStyle::drawRegion(TFlash &flash, const TRegion *r) const +{ + + TRectD bbox(r->getBBox()); + + vector pos; + int posLX, posLY; + TRandom rand; + TPointD quad[4]; + + preaprePos(bbox, pos, posLX, posLY, rand); + if (pos.size() <= 0) + return; + flash.drawRegion(*r, nbClip(posLX, posLY, pos)); + + flash.setThickness(0.0); + int x; + for (x = 2; x < (posLX - 2); x += 2) + for (int y = 1; y < posLY; y++) { + vector lvert; + if ((x % 4) == 2) { + lvert.push_back(pos[(x - 1) * posLY + y]); + lvert.push_back(pos[(x)*posLY + y]); + lvert.push_back(pos[(x + 1) * posLY + y]); + lvert.push_back(pos[(x + 2) * posLY + y]); + lvert.push_back(pos[(x + 1) * posLY + y - 1]); + lvert.push_back(pos[(x)*posLY + y - 1]); + } else { + lvert.push_back(pos[(x - 1) * posLY + y - 1]); + lvert.push_back(pos[(x)*posLY + y - 1]); + lvert.push_back(pos[(x + 1) * posLY + y - 1]); + lvert.push_back(pos[(x + 2) * posLY + y - 1]); + lvert.push_back(pos[(x + 1) * posLY + y]); + lvert.push_back(pos[(x)*posLY + y]); + } + flash.setFillColor(m_pointColor[rand.getInt(0, 6)]); + flash.drawPolyline(lvert); + } + + flash.setFillColor(TSolidColorStyle::getMainColor()); + flash.setThickness(0.0); + double thickn = tcrop(m_thickness, 0.0, 100.0) * 0.01 * 5.0; + if (thickn > 0.001) + for (x = 0; x < (posLX - 1); x++) { + int nb = x % 4; + for (int y = 0; y < posLY; y++) { + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y], thickn, quad)) + drawFlashQuad(flash, quad); + if (y > 0 && nb == 1) + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y - 1], thickn, quad)) + drawFlashQuad(flash, quad); + if (y < (posLY - 1) && nb == 3) + if (getQuadLine(pos[x * posLY + y], pos[(x + 1) * posLY + y + 1], thickn, quad)) + drawFlashQuad(flash, quad); + } + } +} diff --git a/toonz/sources/colorfx/regionstyles.h b/toonz/sources/colorfx/regionstyles.h new file mode 100644 index 0000000..d9fb359 --- /dev/null +++ b/toonz/sources/colorfx/regionstyles.h @@ -0,0 +1,914 @@ + + +#ifndef TDERIVEDREGIONSTYLES_H +#define TDERIVEDREGIONSTYLES_H + +// TnzCore includes +#include "tvectorimage.h" +#include "tregionoutline.h" +#include "tsimplecolorstyles.h" + +#undef DVAPI +#undef DVVAR + +#ifdef COLORFX_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +//====================================================== + +// Forward declarations + +class TRandom; + +//====================================================== + +//============================================================ + +class MovingModifier : public TOutlineStyle::RegionOutlineModifier +{ + TPointD m_move; + +public: + MovingModifier(const TPointD &point) + : m_move(point) {} + + TOutlineStyle::RegionOutlineModifier *clone() const; + + TPointD getMovePoint() const { return m_move; } + + void modify(TRegionOutline &outline) const; + +public: + void loadData(TInputStreamInterface &is) + { + is >> m_move.x >> m_move.y; + } + + void saveData(TOutputStreamInterface &os) const + { + os << m_move.x << m_move.y; + } +}; + +//============================================================ + +class MovingSolidColor : public TSolidColorStyle +{ +public: + MovingSolidColor(const TPixel32 &color, const TPointD &move); + + TColorStyle *clone() const; + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 1125; }; + QString getDescription() const { return QCoreApplication::translate("MovingSolidColor", "Offset"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +//============================================================ + +class DVAPI ShadowStyle : public TSolidColorStyle +{ + TPointD m_shadowDirection; + TPixel32 m_shadowColor; + double m_density; + double m_len; + +public: + ShadowStyle( + const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection = TPointD(-1, -1), + double len = 30.0, + double density = 0.4); + + TColorStyle *clone() const; + + void makeIcon(const TDimension &d); + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + //TPixel32 getMainColor() const {return m_shadowColor; } + //void setMainColor(const TPixel32 &color){ m_shadowColor=color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 1127; }; + QString getDescription() const { return QCoreApplication::translate("ShadowStyle", "Hatched Shading"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + + //it is too slow and if the region is too complex, some flash readers (IExplorer) crash. + // So it's better drawing it as a normal solid color + //void drawRegion( TFlash& flash, const TRegion* r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void drawPolyline(const TColorFunction *cf, vector &polyline, TPointD shadowDirection) const; +}; + +//============================================================ + +class DVAPI ShadowStyle2 : public TSolidColorStyle +{ + TPointD m_shadowDirection; + TPixel32 m_shadowColor; + double m_shadowLength; + +public: + ShadowStyle2( + const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection = TPointD(-1, -1), + double shadowLength = 70.0); + + TColorStyle *clone() const; + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + //TPixel32 getMainColor() const {return m_shadowColor; } + //void setMainColor(const TPixel32 &color){ m_shadowColor=color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 1135; }; + QString getDescription() const { return QCoreApplication::translate("ShadowStyle2", "Plain Shadow"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void drawPolyline(const TColorFunction *cf, const vector &polyline, TPointD shadowDirection) const; + int drawPolyline(TFlash &flash, vector &polyline, + TPointD shadowDirection, const bool isDraw = true) const; +}; + +//============================================================ + +class RubberModifier : public TOutlineStyle::RegionOutlineModifier +{ + double m_deform; + +public: + RubberModifier(double deform) + : m_deform(deform) {} + + void loadData(TInputStreamInterface &is) + { + is >> m_deform; + } + void saveData(TOutputStreamInterface &os) const + { + os << m_deform; + } + + double getDeform() { return m_deform; } + void setDeform(const double deform) { m_deform = deform; } + + void modify(TRegionOutline &outline) const; + + TOutlineStyle::RegionOutlineModifier *clone() const; +}; + +//============================================================ + +class DVAPI TRubberFillStyle : public TSolidColorStyle +{ + typedef vector QuadraticVector; + typedef vector QuadraticPVector; + +public: + TRubberFillStyle(const TPixel32 &color, double deform); + + TColorStyle *clone() const; + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void makeIcon(const TDimension &d); + + int getTagId() const { return 1128; }; + QString getDescription() const { return QCoreApplication::translate("TRubberFillStyle", "Blob"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void transformPolylines(); +}; + +//============================================================ + +class DVAPI TPointShadowFillStyle : public TSolidColorStyle +{ + TPointD m_shadowDirection; + TPixel32 m_shadowColor; + double m_shadowSize; + double m_density; + double m_pointSize; + +public: + TPointShadowFillStyle(const TPixel32 &bgColor, + const TPixel32 &shadowColor, + const TPointD &shadowDirection = TPointD(-1, -1), + double density = 0.1, + double shadowSize = 30.0, + double pointSize = 5.0); + + TColorStyle *clone() const; + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + /* + TPixel32 getMainColor() const {return m_shadowColor; } + void setMainColor(const TPixel32 &color){ m_shadowColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 1129; }; + QString getDescription() const { return QCoreApplication::translate("TPointShadowFillStyle", "Sponge Shading"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + double triangleArea(const TPointD &a, const TPointD &b, const TPointD &c) const; + void shadowOnEdge_parallel(const TPointD &p0, const TPointD &p1, + const TPointD &p2, TRandom &rnd) const; + int shadowOnEdge_parallel(TFlash &flash, + const TPointD &p0, const TPointD &p1, + const TPointD &p2, TRandom &rnd, + const double radius, + const bool isDraw) const; + + void deleteSameVerts(TRegionOutline::Boundary::iterator &rit, + vector &pv) const; +}; + +//============================================================ + +class DVAPI TDottedFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_dotSize; + double m_dotDist; + bool m_isShifted; + +public: + TDottedFillStyle(const TPixel32 &bgColor, + const TPixel32 &pointColor, + const double dotSize, + const double dotDist, + const bool isShifted); + + TDottedFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1130; }; + QString getDescription() const { return QCoreApplication::translate("TDottedFillStyle", "Polka Dots"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + int nbClip(const double LDotDist, const bool LIsShifted, + const TRectD &bbox) const; +}; + +//============================================================ + +class DVAPI TCheckedFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_HDist, m_HAngle; + double m_VDist, m_VAngle, m_Thickness; + +public: + TCheckedFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double HDist, const double HAngle, + const double VDist, const double VAngle, const double Thickness); + + TCheckedFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1131; }; + QString getDescription() const { return QCoreApplication::translate("TCheckedFillStyle", "Square"); } + +private: + void getHThickline(const TPointD &lc, const double lx, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const; + void getVThickline(const TPointD &lc, const double ly, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const; + int nbClip(const TRectD &bbox) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +//============================================================ + +class ArtisticModifier : public TOutlineStyle::RegionOutlineModifier +{ + TPointD m_move; + double m_period; + +public: + ArtisticModifier(const TPointD &point, double period) + : m_move(point), m_period(period) {} + + TOutlineStyle::RegionOutlineModifier *clone() const; + + void loadData(TInputStreamInterface &is) + { + is >> m_move.x >> m_move.y >> m_period; + } + void saveData(TOutputStreamInterface &os) const + { + os << m_move.x << m_move.y << m_period; + } + + TPointD getMovePoint() const { return m_move; } + double getPeriod() const { return m_period; } + + void modify(TRegionOutline &outline) const; +}; + +//============================================================ + +class ArtisticSolidColor : public TSolidColorStyle +{ +public: + ArtisticSolidColor(const TPixel32 &color, const TPointD &move, double period); + + TColorStyle *clone() const; + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 1132; }; + QString getDescription() const { return QCoreApplication::translate("ArtisticSolidColor", "Irregular"); } + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +//============================================================ + +class DVAPI TChalkFillStyle : public TSolidColorStyle +{ + TPixel32 m_color0; + double m_density, m_size; + +public: + TChalkFillStyle(const TPixel32 &color0, const TPixel32 &color1, + const double density, const double size); + TChalkFillStyle(const TPixel32 &color0, const TPixel32 &color1); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_color0; } + void setMainColor(const TPixel32 &color){ m_color0=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + QString getDescription() const { return QCoreApplication::translate("TChalkFillStyle", "Chalk"); } + void loadData(int oldId, TInputStreamInterface &); + void getObsoleteTagIds(vector &ids) const { ids.push_back(1133); } + int getTagId() const { return 1143; }; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +//============================================================ + +class DVAPI TChessFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_HDist, m_VDist, m_Angle; + +public: + TChessFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double HDist, const double VDist, const double Angle); + TChessFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1136; }; + QString getDescription() const { return QCoreApplication::translate("TChessFillStyle", "Chessboard"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void makeGrid(TRectD &bbox, TRotation &rotM, + vector &grid, int &nbClip) const; +}; + +//============================================================ + +class DVAPI TStripeFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_Dist, m_Angle, m_Thickness; + +public: + TStripeFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double Dist, const double Angle, const double Thickness); + TStripeFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1137; }; + QString getDescription() const { return QCoreApplication::translate("TStripeFillStyle", "Banded"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + + void makeIcon(const TDimension &d); + +private: + void getThickline(const TPointD &lc, const double ly, + TPointD &p0, TPointD &p1, + TPointD &p2, TPointD &p3) const; + int nbClip(const TRectD &bbox) const; +}; + +//============================================================ + +class DVAPI TLinGradFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_Angle; + double m_XPos, m_YPos, m_Size; + +public: + TLinGradFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double Angle, const double XPos, const double YPos, + const double Size); + TLinGradFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1138; }; + QString getDescription() const { return QCoreApplication::translate("TLinGradFillStyle", "Linear Gradient"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void getRects(const TRectD &bbox, vector &r0, + vector &r1, vector &r2) const; + + void getRect(const TRectD &bbox, vector &r) const; +}; + +//============================================================ + +class DVAPI TRadGradFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_Radius; + double m_XPos, m_YPos; + double m_Smooth; + +public: + TRadGradFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double XPos, const double YPos, + const double Radius, const double Smooth); + TRadGradFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + // TPixel32 getMainColor() const {return m_pointColor; } + // void setMainColor(const TPixel32 &color){ m_pointColor=color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1139; }; + QString getDescription() const { return QCoreApplication::translate("TRadGradFillStyle", "Radial Gradient"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +//============================================================ + +class DVAPI TCircleStripeFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor; + double m_XPos, m_YPos; + double m_Dist, m_Thickness; + +public: + TCircleStripeFillStyle(const TPixel32 &bgColor, const TPixel32 &pointColor, + const double XPos, const double YPos, + const double Dist, const double Thickness); + TCircleStripeFillStyle(const TPixel32 &color); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor; } + void setMainColor(const TPixel32 &color){ m_pointColor=color; } + */ + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1140; }; + QString getDescription() const { return QCoreApplication::translate("TCircleStripeFillStyle", "Concentric"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void getCircleStripeQuads(const TPointD ¢er, + const double r1, const double r2, + vector &pv) const; + void drawCircleStripe(const TPointD ¢er, + const double r1, const double r2, + const TPixel32 &col) const; +}; + +//============================================================ + +class DVAPI TMosaicFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor[4]; + double m_size; + double m_deform; + double m_minThickness; + double m_maxThickness; + +public: + TMosaicFillStyle(const TPixel32 &bgColor, + const TPixel32 pointColor[4], + const double size, + const double deform, + const double minThickness, + const double maxThickness); + TMosaicFillStyle(const TPixel32 bgColor); + + TColorStyle *clone() const; + + /* + TPixel32 getMainColor() const {return m_pointColor[0]; } + void setMainColor(const TPixel32 &color){ m_pointColor[0]=color; } + */ + + int getColorParamCount() const { return 5; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1141; }; + QString getDescription() const { return QCoreApplication::translate("TMosaicFillStyle", "Stained Glass"); } + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +private: + void preaprePos(const TRectD &box, vector &v, + int &lX, int &lY, TRandom &rand) const; + bool getQuad(const int ix, const int iy, + const int lX, const int lY, + vector &v, TPointD *pquad, TRandom &rand) const; +}; + +//============================================================ + +class DVAPI TPatchFillStyle : public TSolidColorStyle +{ + TPixel32 m_pointColor[6]; + double m_size; + double m_deform; + double m_thickness; + +public: + TPatchFillStyle(const TPixel32 &bgColor, + const TPixel32 pointColor[6], + const double size, + const double deform, + const double thickness); + TPatchFillStyle(const TPixel32 &bgColor); + + TColorStyle *clone() const; + + int getColorParamCount() const { return 7; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + bool isRegionStyle() const { return true; } + bool isStrokeStyle() const { return false; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawRegion(TFlash &flash, const TRegion *r) const; + + int getTagId() const { return 1142; }; + QString getDescription() const { return QCoreApplication::translate("TPatchFillStyle", "Beehive"); } + +private: + void preaprePos(const TRectD &box, vector &v, + int &lX, int &lY, TRandom &rand) const; + bool getQuadLine(const TPointD &a, const TPointD &b, + const double thickn, TPointD *quad) const; + void drawGLQuad(const TPointD *quad) const; + int nbClip(const int lX, const int lY, const vector &v) const; + void drawFlashQuad(TFlash &flash, const TPointD *quad) const; + void drawFlashTriangle(TFlash &flash, const TPointD &p1, + const TPointD &p2, const TPointD &p3) const; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; +}; + +#endif // TDERIVEDREGIONSTYLES_H diff --git a/toonz/sources/colorfx/strokestyles.cpp b/toonz/sources/colorfx/strokestyles.cpp new file mode 100644 index 0000000..92969b9 --- /dev/null +++ b/toonz/sources/colorfx/strokestyles.cpp @@ -0,0 +1,6356 @@ + + +// TnzCore includes +#include "tcolorfunctions.h" +#include "trandom.h" +#include "tflash.h" +#include "tcurves.h" +#include "tvectorrenderdata.h" +#include "tmathutil.h" +#include "colorfxutils.h" +#include "tpixelutils.h" + +// tcg includes +#include "tcg/tcg_misc.h" + +// Qt includes +#include +#include + +#include "strokestyles.h" + +using namespace std; + +#define MINTHICK 1.0 + +//============================================================================= + +namespace +{ + +template +class TOptimizedStrokePropT : public TStrokeProp +{ +protected: + double m_pixelSize; + + TOptimizedStrokeStyleT *m_colorStyle; + T m_data; + +public: + TOptimizedStrokePropT(const TStroke *stroke, TOptimizedStrokeStyleT *style); + ~TOptimizedStrokePropT() { m_colorStyle->release(); } + + const TColorStyle *getColorStyle() const; + + TStrokeProp *clone(const TStroke *stroke) const; + void draw(const TVectorRenderData &rd); + void draw(TFlash &flash) + { + getColorStyle()->drawStroke(flash, getStroke()); + } +}; + +//----------------------------------------------------------------------------- + +template +TOptimizedStrokePropT::TOptimizedStrokePropT(const TStroke *stroke, TOptimizedStrokeStyleT *style) + : TStrokeProp(stroke), m_colorStyle(style), m_pixelSize(0) +{ + m_styleVersionNumber = style->getVersionNumber(); + m_colorStyle->addRef(); +} + +//----------------------------------------------------------------------------- + +template +const TColorStyle *TOptimizedStrokePropT::getColorStyle() const +{ + return m_colorStyle; +} + +//----------------------------------------------------------------------------- + +template +TStrokeProp *TOptimizedStrokePropT::clone(const TStroke *stroke) const +{ + TOptimizedStrokePropT *prop = new TOptimizedStrokePropT(stroke, m_colorStyle); + prop->m_strokeChanged = m_strokeChanged; + prop->m_data = m_data; + return prop; +} + +//----------------------------------------------------------------------------- + +template +void TOptimizedStrokePropT::draw(const TVectorRenderData &rd) /*assenza di const non e' una dimenticanza! Alcune sottoclassi devono ridefinire questo metodo e serbve che non sia const*/ +{ + if (rd.m_clippingRect != TRect() && !rd.m_is3dView && !convert(rd.m_aff * m_stroke->getBBox()).overlaps(rd.m_clippingRect)) + return; + + glPushMatrix(); + tglMultMatrix(rd.m_aff); + + double pixelSize = sqrt(tglGetPixelSize2()); + if (m_strokeChanged || + m_styleVersionNumber != m_colorStyle->getVersionNumber() || + !isAlmostZero(pixelSize - m_pixelSize, 1e-5)) { + m_strokeChanged = false; + m_pixelSize = pixelSize; + m_styleVersionNumber = m_colorStyle->getVersionNumber(); + m_colorStyle->computeData(m_data, m_stroke, rd.m_cf); + } + + m_colorStyle->drawStroke(rd.m_cf, m_data, m_stroke); + + glPopMatrix(); +} +} + +//============================================================================= + +template +TStrokeProp *TOptimizedStrokeStyleT::makeStrokeProp(const TStroke *stroke) +{ + return new TOptimizedStrokePropT(stroke, this); +} + +//============================================================================= + +inline void tglNormal(const T3DPointD &p) +{ + glNormal3d(p.x, p.y, p.z); +} + +inline void tglVertex(const T3DPointD &p) +{ + glVertex3d(p.x, p.y, p.z); +} + +//============================================================================= + +TFurStrokeStyle::TFurStrokeStyle() + : m_color(TPixel32::Black), m_angle(120.0), m_length(1.0), m_cs(0.0), m_sn(0.0) +{ + double rad = TConsts::pi_180 * m_angle; + m_cs = cos(rad); + m_sn = sin(rad); +} + +//----------------------------------------------------------------------------- + +TColorStyle *TFurStrokeStyle::clone() const +{ + return new TFurStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TFurStrokeStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TFurStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TFurStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < getParamCount()); + return index == 0 ? QCoreApplication::translate("TFurStrokeStyle", "Angle") : QCoreApplication::translate("TFurStrokeStyle", "Size"); +} + +//----------------------------------------------------------------------------- + +void TFurStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < getParamCount()); + if (index == 0) { + min = 0.0; + max = 180.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TFurStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < getParamCount()); + return index == 0 ? m_angle : m_length; +} + +//----------------------------------------------------------------------------- + +void TFurStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < getParamCount()); + if (index == 0) { + m_angle = value; + double rad = TConsts::pi_180 * m_angle; + m_cs = cos(rad); + m_sn = sin(rad); + } else + m_length = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TFurStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + using TConsts::pi; + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + double s = 0.0; + double ds = 4; + double vs = 1; + TRandom rnd; + flash.setLineColor(m_color); + vector segmentsArray; + + while (s <= length) { + double w = stroke->getParameterAtLength(s); + TThickPoint pos = stroke->getThickPoint(w); + TPointD pos1 = (TPointD)pos; + + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0.0) { + s += 0.5; + continue; + } + u = normalize(u); + TPointD v = rotate90(u); + + double length = m_length * pos.thick; + vs = -vs; + + double q = 0.01 * (rnd.getFloat() * 2 - 1); + segmentsArray.push_back(TSegment(pos1, pos1 + length * ((m_cs + q) * u + (vs * m_sn) * v))); + s += ds; + } + + flash.drawSegments(segmentsArray, true); +} + +//----------------------------------------------------------------------------- +void TFurStrokeStyle::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + using TConsts::pi; + double length = stroke->getLength(); + + double s = 0.0; + double ds = 4; + double vs = 1; + TRandom rnd; + + positions.clear(); + positions.reserve(tceil(length / ds) + 1); + + while (s <= length) { + double w = stroke->getParameterAtLength(s); + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0.0) { + s += 0.5; + continue; + } + u = normalize(u); + TPointD v = rotate90(u); + + double length = 0; + if (pos.thick) + length = m_length * pos.thick; + else + length = 1.0; + + vs = -vs; + + positions.push_back(pos); + double q = 0.01 * (rnd.getFloat() * 2 - 1); + positions.push_back(pos + length * ((m_cs + q) * u + (vs * m_sn) * v)); + + s += ds; + } +} + +//----------------------------------------------------------------------------- +void TFurStrokeStyle::drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + tglColor(color); + + for (UINT i = 0; i < positions.size(); i += 2) { + glBegin(GL_LINE_STRIP); + tglColor(color); + tglVertex(positions[i]); + glColor4d(1, 1, 1, 0.0); + tglVertex(positions[i + 1]); + glEnd(); + } +} + +//============================================================================= + +TChainStrokeStyle::TChainStrokeStyle(const TPixel32 &color) + : m_color(color) +{ +} + +//----------------------------------------------------------------------------- + +TChainStrokeStyle::TChainStrokeStyle() + : m_color(TPixel32(20, 10, 0)) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TChainStrokeStyle::clone() const +{ + return new TChainStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +void TChainStrokeStyle::computeData(Points &data, const TStroke *stroke, const TColorFunction *cf) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + // spessore della catena = spessore "medio" dello stroke + double thickness = 0.25 * (stroke->getThickPoint(0).thick + + stroke->getThickPoint(1.0 / 3.0).thick + + stroke->getThickPoint(2.0 / 3.0).thick + + stroke->getThickPoint(1).thick); + + if (thickness == 0) + thickness = 0.1; + double ringHeight = thickness; + double ringWidth = 1.5 * ringHeight; + + // distanza fra i centri degli anelli + double ringDistance = 2 * 1.2 * ringWidth; + + // distanza fra il centro dell'anello e il punto di attacco dell'anello trasversale + //double joinPos = 0.45 * ringWidth; + + TPointD oldPos; + //bool firstRing = true; + double s = 0; + + data.clear(); + data.reserve(tceil(length / ringDistance) * 2 + 2); + + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + + data.push_back(pos); + data.push_back(u); + + // se e' il caso unisco la catena alla precedente + s += ringDistance; + } +} + +//----------------------------------------------------------------------------- + +void TChainStrokeStyle::drawStroke(const TColorFunction *cf, Points &data, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + //double length = stroke->getLength(); + + // spessore della catena = spessore "medio" dello stroke + double thickness = 0.25 * (stroke->getThickPoint(0).thick + + stroke->getThickPoint(1.0 / 3.0).thick + + stroke->getThickPoint(2.0 / 3.0).thick + + stroke->getThickPoint(1).thick); + + if (thickness * thickness < 4 * tglGetPixelSize2()) { + TCenterLineStrokeStyle *appStyle = new TCenterLineStrokeStyle(m_color, 0x0, thickness); + appStyle->drawStroke(cf, stroke); + delete appStyle; + return; + } + + assert(thickness); + double ringHeight = thickness; + double ringWidth = 1.5 * ringHeight; + + // distanza fra i centri degli anelli + //double ringDistance = 2 * 1.2 * ringWidth; + + // distanza fra il centro dell'anello e il punto di attacco dell'anello trasversale + double joinPos = 0.45 * ringWidth; + + // definisco la forma dell'anello della catena + GLuint ringId; + ringId = glGenLists(1); + double a = .6, b = .6; + glNewList(ringId, GL_COMPILE); + glPushMatrix(); + glScaled(ringWidth, ringHeight, 1); + glBegin(GL_LINE_STRIP); + glVertex2d(1, b); + glVertex2d(a, 1); + glVertex2d(-a, 1); + glVertex2d(-1, b); + glVertex2d(-1, -b); + glVertex2d(-a, -1); + glVertex2d(a, -1); + glVertex2d(1, -b); + glVertex2d(1, b); + glEnd(); + glPopMatrix(); + glEndList(); + + // disegno la catena + + if (cf) + tglColor((*cf)(m_color)); + else + tglColor(m_color); + + TPointD oldPos; + //bool firstRing = true; + //double s = 0; + for (UINT i = 0; i < data.size(); i += 2) { + TPointD pos = data[i]; + TPointD u = data[i + 1]; + TPointD v = rotate90(u); + + // disegno un anello della catena + glPushMatrix(); + TAffine aff(u.x, v.x, pos.x, u.y, v.y, pos.y); + tglMultMatrix(aff); + glCallList(ringId); + glPopMatrix(); + + // se e' il caso unisco la catena alla precedente + if (i != 0) { + TPointD q = pos - u * joinPos; + tglDrawSegment(oldPos, q); + } + + oldPos = pos + u * joinPos; + } + + glDeleteLists(ringId, 1); +} + +//----------------------------------------------------------------------------- + +void TChainStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + // spessore della catena = spessore "medio" dello stroke + double thickness = 0.25 * (stroke->getThickPoint(0).thick + + stroke->getThickPoint(1.0 / 3.0).thick + + stroke->getThickPoint(2.0 / 3.0).thick + + stroke->getThickPoint(1).thick); + + if (thickness < 2) { + TCenterLineStrokeStyle *appStyle = new TCenterLineStrokeStyle(m_color, 0x0, thickness); + appStyle->drawStroke(flash, stroke); + delete appStyle; + return; + } + + assert(thickness); + double ringHeight = thickness; + double ringWidth = 1.5 * ringHeight; + double ringDistance = 2 * 1.2 * ringWidth; + + double joinPos = 0.45 * ringWidth; + //const int ringId = 124; + double a = .6, b = .6; + + TScale scaleM(ringWidth, ringHeight); + vector chain; + chain.push_back(scaleM * TPointD(1, b)); //0 + chain.push_back(scaleM * TPointD(a, 1)); //1 + chain.push_back(scaleM * TPointD(-a, 1)); //2 + chain.push_back(scaleM * TPointD(-1, b)); //3 + chain.push_back(scaleM * TPointD(-1, -b)); //4 + chain.push_back(scaleM * TPointD(-a, -1)); //5 + chain.push_back(scaleM * TPointD(a, -1)); //6 + chain.push_back(scaleM * TPointD(1, -b)); //7 + chain.push_back(scaleM * TPointD(1, b)); //8 + + /* + chain.push_back( TPointD(1, b)); //0 + chain.push_back( TPointD( a, 1)); //1 + chain.push_back( TPointD(-a, 1)); //2 + chain.push_back( TPointD(-1, b)); //3 + chain.push_back( TPointD(-1,-b)); //4 + chain.push_back( TPointD(-a,-1)); //5 + chain.push_back( TPointD( a,-1)); //6 + chain.push_back( TPointD( 1,-b)); //7 + chain.push_back( TPointD(1, b)); //8 +*/ + + vector chainS; + chainS.push_back(TSegment(chain[0], chain[1])); + chainS.push_back(TSegment(chain[1], chain[2])); + chainS.push_back(TSegment(chain[2], chain[3])); + chainS.push_back(TSegment(chain[3], chain[4])); + chainS.push_back(TSegment(chain[4], chain[5])); + chainS.push_back(TSegment(chain[5], chain[6])); + chainS.push_back(TSegment(chain[6], chain[7])); + chainS.push_back(TSegment(chain[7], chain[0])); + + flash.setLineColor(m_color); + + TPointD oldPos; + bool firstRing = true; + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + // TPointD v = rotate90(u); + + TTranslation translM(pos.x, pos.y); + TRotation rotM(rad2degree(atan(u))); + TAffine tM = translM * rotM; + + // With direct transformation + vector lchainS; + for (int i = 0; i < 8; i++) + lchainS.push_back(TSegment(tM * chain[i], tM * chain[i + 1])); + flash.drawSegments(lchainS, false); + + /* +// With TFlash transformations + flash.pushMatrix(); + flash.multMatrix(translM); + flash.multMatrix(rotM); +// flash.multMatrix(scaleM); + flash.drawSegments(chainS,false); +// if (chainId==-1) +// chainId=flash.drawSegments(chainS,false); +// else +// flash.drawShape(chainId); + flash.popMatrix(); +*/ + + if (!firstRing) { + TPointD q = pos - u * joinPos; + vector sv; + sv.push_back(TSegment(oldPos, q)); + flash.drawSegments(sv, false); + } else + firstRing = false; + + oldPos = pos + u * joinPos; + s += ringDistance; + } +} + +//============================================================================= + +TSprayStrokeStyle::TSprayStrokeStyle() + : m_color(TPixel32::Blue), m_blend(0.5), m_intensity(10.0), m_radius(0.3) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TSprayStrokeStyle::clone() const +{ + return new TSprayStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TSprayStrokeStyle::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TSprayStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TSprayStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TSprayStrokeStyle", "Border Fade"); + break; + case 1: + value = QCoreApplication::translate("TSprayStrokeStyle", "Density"); + break; + case 2: + value = QCoreApplication::translate("TSprayStrokeStyle", "Size"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TSprayStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + min = 0.0; + max = 1.0; + break; + case 1: + min = 0.0; + max = 100.0; + break; + case 2: + min = 0.0; + max = 1.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TSprayStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + double value = 0; + switch (index) { + case 0: + value = m_blend; + break; + case 1: + value = m_intensity; + break; + case 2: + value = m_radius; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TSprayStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + m_blend = value; + break; + case 1: + m_intensity = value; + break; + case 2: + m_radius = value; + break; + } + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TSprayStrokeStyle::drawStroke(const TColorFunction *cf, const TStroke *stroke) const +{ + // TStroke *stroke = getStroke(); + double length = stroke->getLength(); + double step = 4; + + double blend = m_blend; //distanza che controlla da dove il gessetto comincia il fade out (0, 1) + double intensity = m_intensity; //quanti punti vengono disegnati ad ogni step + double radius = m_radius; + double decay = 1 - blend; + bool fill = 0; + TPointD pos1; + TRandom rnd; + TPixelD dcolor; + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + dcolor = toPixelD(color); + double s = 0; + double minthickness = MINTHICK * sqrt(tglGetPixelSize2()); + double thickness = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + TPointD shift; + if (pos.thick < MINTHICK) + thickness = minthickness; + else + thickness = pos.thick; + for (int i = 0; i < intensity; i++) { + double vrandnorm = (0.5 - rnd.getFloat()) * 2; + double randomv = vrandnorm * pos.thick; + double randomu = (0.5 - rnd.getFloat()) * step; + shift = u * randomu + v * randomv; + pos1 = pos + shift; + double mod = fabs(vrandnorm); + if (mod < decay) + glColor4d(dcolor.r, dcolor.g, dcolor.b, rnd.getFloat() * dcolor.m); + else + glColor4d(dcolor.r, dcolor.g, dcolor.b, rnd.getFloat() * (1 - mod) * dcolor.m); + if (fill) + tglDrawDisk(pos1, radius * thickness * rnd.getFloat()); + else + tglDrawCircle(pos1, radius * thickness * rnd.getFloat()); + } + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TSprayStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + double length = stroke->getLength(); + double step = 4; + + double blend = m_blend; //distanza che controlla da dove il gessetto comincia il fade out (0, 1) + double intensity = m_intensity; //quanti punti vengono disegnati ad ogni step + double radius = m_radius; + double decay = 1 - blend; + bool fill = 0; + TPointD pos1; + TRandom rnd; + TPixel32 color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + TPointD shift; + for (int i = 0; i < intensity; i++) { + double vrandnorm = (0.5 - rnd.getFloat()) * 2; + double randomv = vrandnorm * pos.thick; + double randomu = (0.5 - rnd.getFloat()) * step; + shift = u * randomu + v * randomv; + pos1 = pos + shift; + double mod = fabs(vrandnorm); + TPixelD ldcolor = dcolor; + ldcolor.m = mod < decay ? rnd.getFloat() * dcolor.m : rnd.getFloat() * (1 - mod) * dcolor.m; + TPixel32 lcolor; + lcolor = toPixel32(ldcolor); + if (fill) { + flash.setFillColor(lcolor); + double r = radius * pos.thick * rnd.getFloat(); + flash.drawEllipse(pos1, r, r); + } else { + flash.setLineColor(lcolor); + flash.setFillColor(TPixel32(0, 0, 0, 0)); + flash.setThickness(0.5); + double r = radius * pos.thick * rnd.getFloat(); + flash.drawEllipse(pos1, r, r); + } + } + s += step; + } +} + +//============================================================================= + +TGraphicPenStrokeStyle::TGraphicPenStrokeStyle() + : m_color(TPixel32::Black), m_intensity(10.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TGraphicPenStrokeStyle::clone() const +{ + return new TGraphicPenStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TGraphicPenStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TGraphicPenStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TGraphicPenStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TGraphicPenStrokeStyle", "Density"); +} + +//----------------------------------------------------------------------------- + +void TGraphicPenStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 0.0; + max = 10.0; +} + +//----------------------------------------------------------------------------- + +double TGraphicPenStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_intensity; +} + +//----------------------------------------------------------------------------- + +void TGraphicPenStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_intensity = value; + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TGraphicPenStrokeStyle::computeData(DrawmodePointsMatrix &data, + const TStroke *stroke, + const TColorFunction *cf) const +{ + data.clear(); + double length = stroke->getLength(); + double step = 10; + TPointD pos1, pos2; + TRandom rnd; + double intensity = m_intensity; + data.reserve(tceil(length / 10.0)); + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + TPointD shift; + Points tmpPoints; + tmpPoints.clear(); + GLenum drawMode; + for (int i = 0; i < intensity; i++) { + if (pos.thick) { + drawMode = GL_LINES; + tmpPoints.reserve((int)(intensity * 2 + 1)); + double randomv = (0.5 - rnd.getFloat()) * pos.thick; + double randomu = (0.5 - rnd.getFloat()) * step; + shift = randomu * u + randomv * v; + pos1 = pos + shift + v * (pos.thick); + pos2 = pos + shift - v * (pos.thick); + tmpPoints.push_back(pos1); + tmpPoints.push_back(pos2); + } else { + drawMode = GL_POINTS; + tmpPoints.reserve((int)(intensity + 1)); + tmpPoints.push_back((TPointD)pos); + } + } + if (!tmpPoints.empty()) { + assert(drawMode == GL_POINTS || drawMode == GL_LINES); + data.push_back(make_pair(drawMode, tmpPoints)); + } + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TGraphicPenStrokeStyle::drawStroke(const TColorFunction *cf, + DrawmodePointsMatrix &data, + const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + tglColor(color); + + DrawmodePointsMatrix::iterator it1 = data.begin(); + for (; it1 != data.end(); ++it1) { + if (it1->first == GL_LINES) { + Points::iterator it2 = it1->second.begin(); + glBegin(GL_LINES); + for (; it2 != it1->second.end(); ++it2) + tglVertex(*it2); + glEnd(); + } else { + assert(it1->first == GL_POINTS); + Points::iterator it2 = it1->second.begin(); + glBegin(GL_POINTS); + for (; it2 != it1->second.end(); ++it2) + tglVertex(*it2); + glEnd(); + } + } +} + +//----------------------------------------------------------------------------- + +void TGraphicPenStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const + +{ + //TStroke *stroke = getStroke(); + vector segmentsArray; + double length = stroke->getLength(); + double step = 10; + TPointD pos1, pos2; + TRandom rnd; + double intensity = m_intensity; + flash.setLineColor(m_color); + + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + TPointD shift; + for (int i = 0; i < intensity; i++) { + double randomv = (0.5 - rnd.getFloat()) * pos.thick; + double randomu = (0.5 - rnd.getFloat()) * step; + shift = randomu * u + randomv * v; + pos1 = pos + shift + v * (pos.thick); + pos2 = pos + shift - v * (pos.thick); + segmentsArray.push_back(TSegment(pos1, pos2)); + } + s += step; + } + + flash.drawSegments(segmentsArray, false); +} + +//============================================================================= + +namespace +{ +double inline get_line_slope(double meter, double inmax, double linemax, + double outmax) +{ + if (meter <= inmax) + return meter / inmax; + else if (meter <= inmax + linemax) + return 1; + else if (meter <= inmax + linemax + outmax) + return (inmax + linemax - meter) / outmax + 1; + else + return 0; +} +} + +//----------------------------------------------------------------------------- + +TDottedLineStrokeStyle::TDottedLineStrokeStyle() + : m_color(TPixel32::Black), m_in(10.0), m_line(50.0), m_out(10.0), m_blank(10.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TDottedLineStrokeStyle::clone() const +{ + return new TDottedLineStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TDottedLineStrokeStyle::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TDottedLineStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TDottedLineStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TDottedLineStrokeStyle", "Fade In"); + break; + case 1: + value = QCoreApplication::translate("TDottedLineStrokeStyle", "Dash"); + break; + case 2: + value = QCoreApplication::translate("TDottedLineStrokeStyle", "Fade Out"); + break; + case 3: + value = QCoreApplication::translate("TDottedLineStrokeStyle", "Gap"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TDottedLineStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = 1.0; + max = 100.0; + break; + case 1: + min = 1.0; + max = 100.0; + break; + case 2: + min = 1.; + max = 100.0; + break; + case 3: + min = 0.0; + max = 100.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TDottedLineStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + double value = 0; + switch (index) { + case 0: + value = m_in; + break; + case 1: + value = m_line; + break; + case 2: + value = m_out; + break; + case 3: + value = m_blank; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TDottedLineStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + m_in = value; + break; + case 1: + m_line = value; + break; + case 2: + m_out = value; + break; + case 3: + m_blank = value; + break; + } + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TDottedLineStrokeStyle::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + double length = stroke->getLength(); + + double step = 5.0; + double linemax = m_line; + double inmax = (m_in / 100); + double outmax = (m_out / 100); + double blankmax = m_blank; + double total = 0; + TRandom rnd; + + positions.clear(); + positions.reserve(tceil(length / step) + 1); + + TPointD oldPos1, oldPos2, oldPos3, oldPos4, pos1, pos2, pos3, pos4; + //bool firstRing = true; + double s = 0; + double meter = 0; + double center = 0; + double slopetmp = 0; + double minthickness = MINTHICK * sqrt(tglGetPixelSize2()); + double thickness = 0; + double line = 0, in = 0, out = 0, blank = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + + if (pos.thick < MINTHICK) + thickness = minthickness; + else + thickness = pos.thick; + if (meter >= total) { + meter = 0; + + line = linemax * (1 + rnd.getFloat()) * thickness; + if (line > length - s) + line = length - s; + in = inmax * line; + out = outmax * line; + line = line - in - out; + blank = blankmax * (1 + rnd.getFloat()) * thickness; + if (in + out > length) { + in = rnd.getFloat() * (length / 2); + out = length - in; + line = 0; + } + total = in + line + out + blank; + } else if (meter > in + line + out + step) { + s += step; + meter += step; + continue; + } + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + double slope = 0; + slope = get_line_slope(meter, in, line, out); + slopetmp = slope; + TPointD v = rotate90(u) * (thickness)*slope; + if (pos.thick * slope < 1) + center = 0.0; + else + center = 0.5; + pos1 = pos + v; + pos2 = pos + v * 0.5; + pos3 = pos - v * 0.5; + pos4 = pos - v; + + positions.push_back(pos1); + positions.push_back(pos2); + positions.push_back(pos3); + positions.push_back(pos4); + + s += step; + meter += step; + } +} + +//----------------------------------------------------------------------------- + +void TDottedLineStrokeStyle::drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + for (UINT i = 4; i < positions.size(); i += 4) { + glBegin(GL_QUAD_STRIP); + glColor4ub(color.r, color.g, color.b, 0); + tglVertex(positions[i - 4]); + tglVertex(positions[i]); + glColor4ub(color.r, color.g, color.b, color.m); + tglVertex(positions[i - 3]); + tglVertex(positions[i + 1]); + tglVertex(positions[i - 2]); + tglVertex(positions[i + 2]); + glColor4ub(color.r, color.g, color.b, 0); + tglVertex(positions[i - 1]); + tglVertex(positions[i + 3]); + glEnd(); + } +} + +//----------------------------------------------------------------------------- + +void TDottedLineStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + double step = 5.0; + double linemax = m_line; + double inmax = m_in / 100; + double outmax = m_out / 100; + double blankmax = m_blank; + double total = 0; + TRandom rnd; + + TPixel32 color = m_color; + TPixel32 color_transp(color.r, color.g, color.b, 0); + + TPointD oldPos1, oldPos2, oldPos3, oldPos4, pos1, pos2, pos3, pos4; + bool firstRing = true; + double s = 0; + double meter = 0; + double center = 0; + double slopetmp = 0; + double minthickness = MINTHICK; + double thickness = 0; + SFlashUtils sfu; + flash.setThickness(0.0); + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + double line = 0, in = 0, out = 0, blank = 0; + if (pos.thick < MINTHICK) + thickness = minthickness; + else + thickness = pos.thick; + + if (meter >= total) { + meter = 0; + + line = linemax * (1 + rnd.getFloat()) * thickness; + if (line > length - s) + line = length - s; + in = inmax * line; + out = outmax * line; + line = line - in - out; + blank = blankmax * (1 + rnd.getFloat()) * thickness; + /* --- OLD Version --- + line=linemax*(1+rnd.getFloat()); + in=inmax*(1+rnd.getFloat())*pos.thick; + out=outmax*(1+rnd.getFloat())*pos.thick; + blank=blankmax*(1+rnd.getFloat())*pos.thick; +*/ + if (in + out > length) { + in = rnd.getFloat() * (length / 2); + out = length - in; + line = 0; + } + total = in + line + out + blank; + } else if (meter > in + line + out + step) { + s += step; + meter += step; + continue; + } + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + double slope = 0; + if (s <= length - out) { + slope = get_line_slope(meter, in, line, out); + slopetmp = slope; + } else + slope = (length - s) * (slopetmp / out); + TPointD v = rotate90(u) * (pos.thick) * slope; + if (pos.thick * slope < 1) + center = 0.0; + else + center = 0.5; + pos1 = pos + v; + pos2 = pos + v * 0.5; + pos3 = pos - v * 0.5; + pos4 = pos - v; + if (firstRing) { + firstRing = false; + } else { + + vector pv; + pv.push_back(oldPos1); + pv.push_back(pos1); + pv.push_back(pos2); + pv.push_back(oldPos2); + sfu.drawGradedPolyline(flash, pv, color_transp, color); + + pv.clear(); + pv.push_back(oldPos2); + pv.push_back(pos2); + pv.push_back(pos3); + pv.push_back(oldPos3); + flash.setFillColor(color); + flash.drawPolyline(pv); + + pv.clear(); + pv.push_back(oldPos3); + pv.push_back(pos3); + pv.push_back(pos4); + pv.push_back(oldPos4); + sfu.drawGradedPolyline(flash, pv, color, color_transp); + } + oldPos1 = pos1; + oldPos2 = pos2; + oldPos3 = pos3; + oldPos4 = pos4; + s += step; + meter += step; + } +} + +//============================================================================= + +TRopeStrokeStyle::TRopeStrokeStyle() + : m_color(TPixel32(255, 135, 0)), m_bend(0.4) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TRopeStrokeStyle::clone() const +{ + return new TRopeStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TRopeStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TRopeStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TRopeStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + return QCoreApplication::translate("TRopeStrokeStyle", "Tilt"); +} + +//----------------------------------------------------------------------------- + +void TRopeStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = -1.0; + max = 1.0; +} + +//----------------------------------------------------------------------------- + +double TRopeStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_bend; +} + +//----------------------------------------------------------------------------- + +void TRopeStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_bend = value; + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TRopeStrokeStyle::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + double length = stroke->getLength(); + // spessore della catena = spessore "medio" dello stroke + double step = 10.0; + double bend; + double bump; + double bump_max = step / 4; + + positions.clear(); + positions.reserve(tceil(length / step) + 1); + + TPointD oldPos1, oldPos2; + bool firstRing = true; + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + bend = pos.thick * m_bend; + bump = pos.thick * 0.3; + if (bump >= bump_max) + bump = bump_max; + TPointD v = rotate90(u) * pos.thick; + TPointD v1 = v * 0.2; + if (firstRing) { + firstRing = false; + } else { + positions.push_back(pos + (bend + bump) * u + v - v1); + positions.push_back(pos + (bend)*u + v); + positions.push_back(oldPos1 + (bump)*u + v1); + positions.push_back(oldPos1); + positions.push_back(oldPos2); + positions.push_back(oldPos2 + bump * u - v1); + positions.push_back(pos + u * (-bend) - v); + positions.push_back(pos + u * (bump - bend) - v + v1); + } + oldPos1 = pos + (bend + bump) * u + v - v1; + oldPos2 = pos + u * (bump - bend) - v + v1; + s += step; + } + positions.push_back(oldPos1); + positions.push_back(oldPos2); +} + +//----------------------------------------------------------------------------- + +void TRopeStrokeStyle::drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const +{ + if (positions.size() <= 1) + return; + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + // GLuint rope_id; + + TPixel32 blackcolor(TPixel32::Black); + if (cf) + blackcolor = (*(cf))(blackcolor); + else + blackcolor = blackcolor; + + //rope_id = glGenLists(1); + + static const int stride = sizeof(TPointD); + glEnableClientState(GL_VERTEX_ARRAY); + + UINT i = 0; + for (; i < positions.size() - 2; i += 8) { + /* + glNewList(rope_id,GL_COMPILE); + tglVertex(positions[i]); + tglVertex(positions[i+1]); + tglVertex(positions[i+2]); + tglVertex(positions[i+3]); + tglVertex(positions[i+4]); + tglVertex(positions[i+5]); + tglVertex(positions[i+6]); + tglVertex(positions[i+7]); + glEndList(); + */ + tglColor(color); + //glBegin(GL_POLYGON); + + glVertexPointer(2, GL_DOUBLE, stride, &positions[i]); + glDrawArrays(GL_POLYGON, 0, 8); + + //glCallList(rope_id); + //glEnd(); + + tglColor(blackcolor); + + //glBegin(GL_LINE_STRIP); + glVertexPointer(2, GL_DOUBLE, stride, &positions[i]); + glDrawArrays(GL_LINE_STRIP, 0, 8); + + //glCallList(rope_id); + //glEnd(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + + glBegin(GL_LINE_STRIP); + tglVertex(positions[i]); + tglVertex(positions[i + 1]); + glEnd(); + + //glDeleteLists(rope_id,1); +} + +//----------------------------------------------------------------------------- + +void TRopeStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + // spessore della catena = spessore "medio" dello stroke + + double step = 10.0; + double bend; + double bump; + double bump_max = step / 4; + + TPixel32 color = m_color; + TPixel32 blackcolor(TPixel32::Black); + + TPointD oldPos1, oldPos2; + bool firstRing = true; + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + bend = pos.thick * m_bend; + bump = pos.thick * 0.3; + if (bump >= bump_max) + bump = bump_max; + TPointD v = rotate90(u) * pos.thick; + TPointD v1 = v * 0.2; + if (firstRing) { + firstRing = false; + } else { + const int nbpp = 8; + TPointD pp[nbpp]; + pp[0] = (pos + (bend + bump) * u + v - v1); + pp[1] = (pos + (bend)*u + v); + pp[2] = (oldPos1 + (bump)*u + v1); + pp[3] = (oldPos1); + pp[4] = (oldPos2); + pp[5] = (oldPos2 + bump * u - v1); + pp[6] = (pos + u * (-bend) - v); + pp[7] = (pos + u * (bump - bend) - v + v1); + + vector pv; + int i; + for (i = 0; i < nbpp; i++) + pv.push_back(pp[i]); + + flash.setFillColor(color); + flash.drawPolyline(pv); + + vector sv; + for (i = 0; i < (nbpp - 1); i++) + sv.push_back(TSegment(pp[i], pp[i + 1])); + flash.setThickness(1.0); + flash.setLineColor(blackcolor); + flash.drawSegments(sv, false); + } + oldPos1 = pos + (bend + bump) * u + v - v1; + oldPos2 = pos + u * (bump - bend) - v + v1; + s += step; + } + + vector sv; + sv.push_back(TSegment(oldPos1, oldPos2)); + flash.setLineColor(blackcolor); + flash.drawSegments(sv, false); +} + +//============================================================================= + +TCrystallizeStrokeStyle::TCrystallizeStrokeStyle() + : m_color(TPixel32(255, 150, 150, 255)), m_period(10.0), m_opacity(0.5) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TCrystallizeStrokeStyle::clone() const +{ + return new TCrystallizeStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TCrystallizeStrokeStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TCrystallizeStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TCrystallizeStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? QCoreApplication::translate("TCrystallizeStrokeStyle", "Crease") : QCoreApplication::translate("TCrystallizeStrokeStyle", "Opacity"); +} + +//----------------------------------------------------------------------------- + +void TCrystallizeStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 1.0; + max = 100.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TCrystallizeStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? m_period : m_opacity; +} + +//----------------------------------------------------------------------------- + +void TCrystallizeStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + if (index == 0) + m_period = value; + else + m_opacity = value; + + //updateVersionNumber(); non serve perche' i parametri vengono sfrutttati direttamente nella draw +} + +//----------------------------------------------------------------------------- + +void TCrystallizeStrokeStyle::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + double length = stroke->getLength(); + double step = 10.0; + TRandom rnd; + positions.clear(); + positions.reserve(tceil((length + 1) / step)); + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * (pos.thick / 2); + positions.push_back(pos + v * (1 + rnd.getFloat()) + u * 2 * rnd.getFloat()); + positions.push_back(pos - v * (1 + rnd.getFloat()) - u * 2 * rnd.getFloat()); + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TCrystallizeStrokeStyle::drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const +{ + //double length = stroke->getLength(); + double step = 10.0; + double period = (101 - m_period) * step; + double counter = 0; + double opacity = m_opacity; + TRandom rnd; + + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + TPixelD dcolor = toPixelD(color); + //double s = 0; + + glBegin(GL_QUAD_STRIP); + for (int i = 0; i < (int)positions.size() / 2; i++) { + if (counter > period) + counter = 0; + glColor4d(dcolor.r, dcolor.g, dcolor.b, (opacity + (counter / period) * rnd.getFloat()) * dcolor.m); + tglVertex(positions[i * 2]); + tglVertex(positions[i * 2 + 1]); + counter += step; + } + glEnd(); + counter = 0; + glColor4d(dcolor.r, dcolor.g, dcolor.b, dcolor.m); + for (int j = 1; j < (int)positions.size() / 2; j++) { + glBegin(GL_LINES); + tglVertex(positions[(j - 1) * 2]); + tglVertex(positions[j * 2]); + glEnd(); + glBegin(GL_LINES); + tglVertex(positions[(j - 1) * 2 + 1]); + tglVertex(positions[j * 2 + 1]); + glEnd(); + } +} + +//----------------------------------------------------------------------------- + +void TCrystallizeStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + double length = stroke->getLength(); + double step = 10.0; + double period = m_period * step; + double counter = 0; + double opacity = m_opacity; + TRandom rnd; + // const double flashGrad=16384.0; + + TPixel32 color = m_color; + + TPixelD dcolor = toPixelD(color); + vector points1; + vector points2; + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * (pos.thick / 2); + points1.push_back(pos + v * (1 + rnd.getFloat()) + u * 2 * rnd.getFloat()); + points2.push_back(pos - v * (1 + rnd.getFloat()) - u * 2 * rnd.getFloat()); + s += step; + } + + // Just for the polygon grading function + // SRegionDrawInFlash rdf; + SFlashUtils sfu; + TPixelD ldcolorPrev = dcolor; + ldcolorPrev.m = (opacity + (0.0 / period) * rnd.getFloat()) * dcolor.m; + flash.setThickness(0.0); + for (int i = 0; i < (int)(points1.size() - 1); i++) { + if (counter > period) + counter = 0; + TPixelD ldcolor = dcolor; + ldcolor.m = (opacity + (counter / period) * rnd.getFloat()) * dcolor.m; + + TPixel32 lcolorPrev; + lcolorPrev = toPixel32(ldcolorPrev); + TPixel32 lcolor; + lcolor = toPixel32(ldcolor); + + vector tpv; + tpv.push_back(points1[i]); + tpv.push_back(points2[i]); + tpv.push_back(points2[i + 1]); + tpv.push_back(points1[i + 1]); + + // Solid Color version + // flash.setFillColor(blend(lcolorPrev,lcolor,0.5)); + // flash.drawPolyline(tpv); + sfu.drawGradedPolyline(flash, tpv, lcolorPrev, lcolor); + + counter += step; + ldcolorPrev = ldcolor; + } + + counter = 0; + vector tsv1, tsv2; + for (int j = 1; j < (int)points1.size(); j++) { + tsv1.push_back(TSegment(points1[j - 1], points1[j])); + tsv2.push_back(TSegment(points2[j - 1], points2[j])); + } + flash.setThickness(1.0); + flash.setLineColor(color); + flash.drawSegments(tsv1, false); + flash.drawSegments(tsv2, false); +} + +//============================================================================= + +namespace +{ + +class Stripe +{ +public: + TPointD oldpos1; + TPointD oldpos2; + TPointD pos1; + TPointD pos2; + int phase; + TPixel32 color; + Stripe(); + void drawpolygon(); + void drawpolygon(TFlash &flash); + void drawlines(TPixel32 blackcolor); + void addToSegment(vector *sv, vector &scontour, TPixel32 *colors); +}; + +Stripe::Stripe() +{ + oldpos1 = TPointD(0, 0); + oldpos2 = TPointD(0, 0); + pos1 = TPointD(0, 0); + pos2 = TPointD(0, 0); + color = TPixel32(0, 0, 0); + phase = 0; +} + +void Stripe::drawpolygon() +{ + + tglColor(color); + glBegin(GL_POLYGON); + tglVertex(oldpos1); + tglVertex(pos1); + tglVertex(pos2); + tglVertex(oldpos2); + glEnd(); +} + +void Stripe::drawpolygon(TFlash &flash) +{ + vector pv; + pv.push_back(oldpos1); + pv.push_back(oldpos2); + pv.push_back(pos2); + pv.push_back(pos1); + flash.setThickness(0); + flash.setFillColor(color); + flash.drawPolyline(pv); + + // Draws the black contour + flash.setThickness(0.5); + flash.setLineColor(TPixel32::Black); + vector sv; + sv.push_back(TSegment(oldpos1, pos1)); + sv.push_back(TSegment(oldpos2, pos2)); + flash.drawSegments(sv, false); + + // It is better, but the flash.drawLine() is missing in my SDK + /* flash.setThickness(0.5); + flash.setLineColor(TPixel32::Black); + flash.drawLine(oldpos1,pos1); + flash.drawLine(oldpos2,pos2); +*/ +} + +void Stripe::addToSegment(vector *sv, vector &scontour, + TPixel32 *colors) +{ + TPointD p0 = (oldpos1 + oldpos2) * 0.5; + TPointD p1 = (pos1 + pos2) * 0.5; + // TPointD p0=oldpos1; + // TPointD p1=pos1; + if (color == colors[0]) + sv[0].push_back(TSegment(p0, p1)); + if (color == colors[1]) + sv[1].push_back(TSegment(p0, p1)); + if (color == colors[2]) + sv[2].push_back(TSegment(p0, p1)); + + scontour.push_back(TSegment(oldpos1, pos1)); + scontour.push_back(TSegment(oldpos2, pos2)); +} + +void Stripe::drawlines(TPixel32 blackcolor) +{ + tglColor(blackcolor); + glBegin(GL_LINE_STRIP); + tglVertex(oldpos1); + tglVertex(pos1); + glEnd(); + glBegin(GL_LINE_STRIP); + tglVertex(pos2); + tglVertex(oldpos2); + glEnd(); +} +} + +//----------------------------------------------------------------------------- + +TBraidStrokeStyle::TBraidStrokeStyle() + : m_period(80.0) + +{ + m_colors[0] = TPixel32::Red; + m_colors[1] = TPixel32::Green; + m_colors[2] = TPixel32::Blue; +} + +//----------------------------------------------------------------------------- + +TColorStyle *TBraidStrokeStyle::clone() const +{ + return new TBraidStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TBraidStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TBraidStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TBraidStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TBraidStrokeStyle", "Twirl"); +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 1.0; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TBraidStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_period; +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_period = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +TPixel32 TBraidStrokeStyle::getColorParamValue(int index) const +{ + TPixel32 tmp; + switch (index) { + case (0): + tmp = m_colors[0]; + break; + case (1): + tmp = m_colors[1]; + break; + case (2): + tmp = m_colors[2]; + break; + } + return tmp; +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::setColorParamValue(int index, const TPixel32 &color) +{ + switch (index) { + case (0): + m_colors[0] = color; + break; + case (1): + m_colors[1] = color; + break; + case (2): + m_colors[2] = color; + break; + } +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 112) + throw TException("Braid stroke style: unknown obsolete format"); + + is >> m_colors[0] >> m_period; + + m_period /= 10.0; + m_colors[0] = TPixel32::Red; + m_colors[1] = TPixel32::Green; + m_colors[2] = TPixel32::Blue; +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::drawStroke(const TColorFunction *cf, const TStroke *stroke) const +{ + double length = stroke->getLength(); + const int ntick = 81; + const double stripethickness = 0.3; + int period = (int)(101 - m_period) * 20; + double step = period / (double)ntick; + double freq = 2 * TConsts::pi / ntick; + int swapcount = 0; + int count = 0; + bool firstRing = true; + double s = 0; + double swap; + vector braid; + vector ssin; + int k = 0; + TPixel32 colors[3]; + for (k = 0; k < 3; k++) { + if (cf) + colors[k] = (*(cf))(m_colors[k]); + else + colors[k] = m_colors[k]; + } + TPixel32 blackcolor = TPixel32::Black; + if (cf) + blackcolor = (*(cf))(blackcolor); + + for (k = 0; k < 3; k++) { + Stripe tmp; + tmp.phase = (ntick * k) / 3; + tmp.color = colors[k]; + braid.push_back(tmp); + } + + for (int z = 0; z < ntick; z++) { + double tmpsin = sin(z * freq); + ssin.push_back(tmpsin); + } + while (s <= length) { + count++; + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * pos.thick; + TPointD v1 = v * stripethickness; + v = v * 0.5; + //int modper=(int)s%(int)period; + if (firstRing) { + firstRing = false; + swap = 0; + for (int j = 0; j < (int)braid.size(); j++) { + int tmp = (count + braid[j].phase) % ntick; + braid[j].oldpos1 = pos + v * ssin[tmp]; + braid[j].oldpos2 = pos + v * ssin[tmp] + v1; + } + } else { + for (int i = 0; i < (int)braid.size(); i++) { + int tmp = (count + braid[i].phase) % ntick; + braid[i].pos1 = pos + v * ssin[tmp]; + braid[i].pos2 = pos + v * ssin[tmp] + v1; + braid[i].drawpolygon(); + braid[i].drawlines(blackcolor); + braid[i].oldpos1 = pos + v * ssin[tmp]; + braid[i].oldpos2 = pos + v * ssin[tmp] + v1; + } + } + s += step; + swap += step; + if (swap > (period / 3.0)) { + swapcount++; + tswap(braid[0], braid[1 + (swapcount & 1)]); + swap -= period / 3.0; + } + } +} + +//----------------------------------------------------------------------------- + +void TBraidStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + double length = stroke->getLength(); + const int ntick = 162; + const double stripethickness = 0.3; + int period = (int)(101 - m_period) * 20; + double step = period / (double)ntick; + double freq = 2 * TConsts::pi / ntick; + int swapcount = 0; + int count = 0; + bool firstRing = true; + double s = 0; + double swap; + vector braid; + vector ssin; + int k = 0; + TPixel32 colors[3]; + + for (k = 0; k < 3; k++) + colors[k] = m_colors[k]; + + TPixel32 blackcolor = TPixel32::Black; + + for (k = 0; k < 3; k++) { + Stripe tmp; + tmp.phase = (ntick * k) / 3; + tmp.color = colors[k]; + braid.push_back(tmp); + } + + for (int z = 0; z < ntick; z++) { + double tmpsin = sin(z * freq); + ssin.push_back(tmpsin); + } + + while (s <= length) { + count++; + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * pos.thick; + TPointD v1 = v * stripethickness; + v = v * 0.5; + //int modper=(int)s%(int)period; + if (firstRing) { + firstRing = false; + swap = 0; + for (int j = 0; j < (int)braid.size(); j++) { + int tmp = (count + braid[j].phase) % ntick; + braid[j].oldpos1 = pos + v * ssin[tmp]; + braid[j].oldpos2 = pos + v * ssin[tmp] + v1; + } + } else { + for (int i = 0; i < (int)braid.size(); i++) { + int tmp = (count + braid[i].phase) % ntick; + braid[i].pos1 = pos + v * ssin[tmp]; + braid[i].pos2 = pos + v * ssin[tmp] + v1; + + braid[i].drawpolygon(flash); + + braid[i].oldpos1 = pos + v * ssin[tmp]; + braid[i].oldpos2 = pos + v * ssin[tmp] + v1; + } + } + s += step; + swap += step; + if (swap > (period / 3.0)) { + swapcount++; + tswap(braid[0], braid[1 + (swapcount & 1)]); + swap -= period / 3.0; + } + } +} + +//============================================================================= + +TSketchStrokeStyle::TSketchStrokeStyle() + : m_color(TPixel32(100, 100, 150, 127)), m_density(0.4) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TSketchStrokeStyle::clone() const +{ + return new TSketchStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TSketchStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TSketchStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TSketchStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TSketchStrokeStyle", "Density"); +} + +//----------------------------------------------------------------------------- + +void TSketchStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 0.0; + max = 1.0; +} + +//----------------------------------------------------------------------------- + +double TSketchStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_density; +} + +//----------------------------------------------------------------------------- + +void TSketchStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_density = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TSketchStrokeStyle::drawStroke(const TColorFunction *cf, const TStroke *stroke) const +{ + double length = stroke->getLength(); + if (length <= 0) + return; + + int count = (int)(length * m_density); + + double maxDw = tmin(1.0, 20.0 / length); + double minDw = 1.0 / length; + + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + tglColor(color); + + TRandom rnd; + + for (int i = 0; i < count; i++) { + double r = rnd.getFloat(); + double dw = (1 - r) * minDw + r * maxDw; + double wmin = dw, wmax = 1 - dw; + if (wmin >= wmax) + continue; + r = rnd.getFloat(); + double w = (1 - r) * wmin + r * wmax; + + double w0 = w - dw; + double w1 = w + dw; + + TThickPoint p0 = stroke->getThickPoint(w0); + TThickPoint p1 = stroke->getThickPoint(w1); + double d01 = tdistance(p0, p1); + if (d01 == 0) + continue; + + int count = (int)(d01); + + TPointD v0 = stroke->getSpeed(w0); + TPointD v1 = stroke->getSpeed(w1); + + if (norm2(v0) == 0 || norm2(v1) == 0) + continue; // non dovrebbe succedere mai, ma.... + v0 = rotate90(normalize(v0)); + v1 = rotate90(normalize(v1)); + + double delta = 0.5 * (rnd.getFloat() - 0.5) * (p0.thick + p1.thick); + double d = 0.1 * d01; + double delta0 = delta - d; + double delta1 = delta + d; + + glBegin(GL_LINE_STRIP); + tglVertex(p0 + v0 * delta0); + for (int j = 1; j < count; j++) { + double t = j / (double)count; + w = (1 - t) * w0 + t * w1; + TPointD v = rotate90(normalize(stroke->getSpeed(w))); + assert(0 <= w && w <= 1); + TPointD p = stroke->getPoint(w); + double delta_t = (1 - t) * delta0 + t * delta1; + tglVertex(p + v * delta_t); + } + tglVertex(p1 + v1 * delta1); + glEnd(); + } + glColor4d(0, 0, 0, 1); +} + +//----------------------------------------------------------------------------- + +void TSketchStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + double length = stroke->getLength(); + if (length <= 0) + return; + vector quadsArray; + + int count = (int)(length * m_density); + + double maxDw = tmin(1.0, 20.0 / length); + double minDw = 1.0 / length; + TPixel color(m_color.r, m_color.g, m_color.b, m_color.m); + flash.setLineColor(color); + + TRandom rnd; + + for (int i = 0; i < count; i++) { + double r = rnd.getFloat(); + double dw = (1 - r) * minDw + r * maxDw; + double wmin = dw, wmax = 1 - dw; + if (wmin >= wmax) + continue; + r = rnd.getFloat(); + double w = (1 - r) * wmin + r * wmax; + + double w0 = w - dw; + double w1 = w + dw; + + TThickPoint p0 = stroke->getThickPoint(w0); + TThickPoint p1 = stroke->getThickPoint(w1); + double d01 = tdistance(p0, p1); + if (d01 == 0) + continue; + + //int count = (int)(d01); + + TPointD v0 = stroke->getSpeed(w0); + TPointD v1 = stroke->getSpeed(w1); + + if (norm2(v0) == 0 || norm2(v1) == 0) + continue; // non dovrebbe succedere mai, ma.... + v0 = rotate90(normalize(v0)); + v1 = rotate90(normalize(v1)); + + double delta = 0.5 * (rnd.getFloat() - 0.5) * (p0.thick + p1.thick); + double d = 0.1 * d01; + double delta0 = delta - d; + double delta1 = delta + d; + + TPointD v = rotate90(normalize(stroke->getSpeed(0.5 * (w0 + w1)))); + TPointD p = stroke->getPoint(0.5 * (w0 + w1)); + double delta_t = 0.5 * (delta0 + delta1); + //quadsArray.push_back(TSegment(p0 + v0*delta0, p1 + v1*delta1)); + TPointD pp0 = p0 + v0 * delta0; + TPointD pp2 = p1 + v1 * delta1; + TPointD pp1 = 2 * (p + v * delta_t) - 0.5 * (pp0 + pp2); //punto p1 ottenuto imponendo che la quad passi per il punto p in t=.5 + quadsArray.push_back(TQuadratic(pp0, pp1, pp2)); + } + flash.drawquads(quadsArray); +} + +//============================================================================= + +TBubbleStrokeStyle::TBubbleStrokeStyle() + : m_color0(TPixel32::Red), m_color1(TPixel32::Green) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TBubbleStrokeStyle::clone() const +{ + return new TBubbleStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +void TBubbleStrokeStyle::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 114) + throw TException("Bubble stroke style: unknown obsolete format"); + + m_color0 = TPixel32::Red; + m_color1 = TPixel32::Green; +} + +//----------------------------------------------------------------------------- + +void TBubbleStrokeStyle::drawStroke(const TColorFunction *cf, const TStroke *stroke) const +{ + double length = stroke->getLength(); + if (length <= 0) + return; + + TRandom rnd(0); + + TPixel32 color0, color1; + if (cf) { + color0 = (*(cf))(m_color0); + color1 = (*(cf))(m_color1); + } else { + color0 = m_color0; + color1 = m_color1; + } + + double minthickness = MINTHICK * sqrt(tglGetPixelSize2()); + double thickness = 0; + for (double s = 0; s < length; s += 5) { + TPointD p = stroke->getPointAtLength(s); + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + int toff = rnd.getInt(0, 999); + int t = (m_currentFrame + toff) % 1000; + TRandom rnd2(t >> 2); + p += 2 * TPointD(-0.5 + rnd2.getFloat(), -0.5 + rnd2.getFloat()); + if (pos.thick < MINTHICK) + thickness = minthickness; + else + thickness = pos.thick; + tglColor(blend(color0, color1, rnd.getFloat())); + double radius = (t & ((int)(thickness))); + tglDrawCircle(p, radius); + } +} + +//----------------------------------------------------------------------------- + +void TBubbleStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + double length = stroke->getLength(); + if (length <= 0) + return; + + TRandom rnd(0); + static int count = 0; + count++; + + TPixel32 color0 = m_color0; + TPixel32 color1 = m_color1; + + for (double s = 0; s < length; s += 5) { + TPointD p = stroke->getPointAtLength(s); + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + int toff = rnd.getInt(0, 999); + int t = (count + toff) % 1000; + TRandom rnd2(t >> 2); + p += 2 * TPointD(-0.5 + rnd2.getFloat(), -0.5 + rnd2.getFloat()); + double r = (t & ((int)(pos.thick))); + flash.setThickness(0.5); + flash.setLineColor(blend(color0, color1, rnd.getFloat())); + flash.setFillColor(TPixel32(0, 0, 0, 0)); + flash.drawEllipse(p, r, r); + } +} + +//============================================================================= + +TTissueStrokeStyle::TTissueStrokeStyle() + : m_color(TPixel32::Black), m_density(3.0), m_border(1.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TTissueStrokeStyle::clone() const +{ + return new TTissueStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TTissueStrokeStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TTissueStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TTissueStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? QCoreApplication::translate("TTissueStrokeStyle", "Density") : QCoreApplication::translate("TTissueStrokeStyle", "Border Size"); +} + +//----------------------------------------------------------------------------- + +void TTissueStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 2.0; + max = 10.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TTissueStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? m_density : m_border; +} + +//----------------------------------------------------------------------------- + +void TTissueStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + if (index == 0) + m_density = value; + else + m_border = value; + + updateVersionNumber(); +} +//----------------------------------------------------------------------------- + +void TTissueStrokeStyle::computeData(PointMatrix &data, + const TStroke *stroke, + const TColorFunction *cf) const +{ + data.clear(); + double length = stroke->getLength(); + double step = 5.0; + double border = m_border; + TPointD pos1, oldPos1; + TRandom rnd; + double increment = 0.0; + int intensity = (int)m_density + 2; + vector points; + vector oldpoints; + double s = 0; + bool firstRing = true; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + increment = (2 * pos.thick) / (intensity - 1); + for (int i = 1; i < intensity - 1; i++) { + pos1 = pos + v * (-pos.thick + i * increment); + points.push_back(pos1); + } + if (firstRing) { + firstRing = false; + } else { + Points tmpPoints1; + tmpPoints1.clear(); + tmpPoints1.reserve(intensity); + + for (int i = 1; i < intensity - 1; i++) { + pos1 = points[i - 1]; + oldPos1 = oldpoints[i - 1]; + tmpPoints1.push_back(oldPos1); + tmpPoints1.push_back(pos1); + } + data.push_back(tmpPoints1); + if (increment > 1) { + int count = tceil(step / increment + 1); + Points tmpPoints2; + tmpPoints2.clear(); + tmpPoints2.reserve(count); + double startpoint = -step - increment / 2.0; + for (int j = 1; j < step / increment + 1; j++) { + tmpPoints2.push_back(points[0] - v * border * increment * rnd.getFloat() + u * (startpoint + j * (increment))); + tmpPoints2.push_back(points[intensity - 3] + v * border * increment * rnd.getFloat() + u * (startpoint + j * (increment))); + } + data.push_back(tmpPoints2); + } + } + oldpoints = points; + points.clear(); + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TTissueStrokeStyle::drawStroke(const TColorFunction *cf, + PointMatrix &data, + const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + tglColor(color); + + PointMatrix::iterator it1 = data.begin(); + for (; it1 != data.end(); ++it1) { + glBegin(GL_LINES); + Points::iterator it2 = (*it1).begin(); + for (; it2 != (*it1).end(); ++it2) { + tglVertex(*it2); + } + glEnd(); + } +} + +//----------------------------------------------------------------------------- + +void TTissueStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + + double length = stroke->getLength(); + double step = 5.0; + double border = m_border; + TPointD pos1, oldPos1; + TRandom rnd; + double increment = 0.0; + int intensity = (int)m_density + 2; + vector points; + vector oldpoints; + TPixel32 color = m_color; + + flash.setLineColor(m_color); + flash.setThickness(1.0); + double s = 0; + bool firstRing = true; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + increment = (2 * pos.thick) / (intensity - 1); + for (int i = 1; i < intensity - 1; i++) { + pos1 = pos + v * (-pos.thick + i * increment); + points.push_back(pos1); + } + if (firstRing) { + firstRing = false; + } else { + flash.setThickness(1.5); + vector sv; + for (int i = 1; i < intensity - 1; i++) { + pos1 = points[i - 1]; + oldPos1 = oldpoints[i - 1]; + sv.push_back(TSegment(oldPos1, pos1)); + } + flash.drawSegments(sv, false); + + if (increment > 1) { + sv.clear(); + double startpoint = -step - increment / 2.0; + for (int j = 1; j < step / increment + 1; j++) { + TPointD p0 = points[0] - v * border * increment * rnd.getFloat() + u * (startpoint + j * (increment)); + TPointD p1 = points[intensity - 3] + v * border * increment * rnd.getFloat() + u * (startpoint + j * (increment)); + // vector sv; + sv.push_back(TSegment(p0, p1)); + } + flash.drawSegments(sv, false); + } + } + oldpoints = points; + points.clear(); + s += step; + } +} + +//============================================================================= + +TBiColorStrokeStyle::TBiColorStrokeStyle() + : m_color0(TPixel32::Red), m_color1(TPixel32::Black) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TBiColorStrokeStyle::clone() const +{ + return new TBiColorStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::loadData(TInputStreamInterface &is) +{ + is >> m_color0 >> m_color1 >> m_parameter; +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::saveData(TOutputStreamInterface &os) const +{ + os << m_color0 << m_color1 << m_parameter; +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 115 && ids != 119) + throw TException("Bicolor stroke style: unknown obsolete format"); + + is >> m_color0 >> m_parameter; + + m_color1 = TPixel32::Black; +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, + const TStroke *stroke) const +{ + TPixel32 color0 = m_color0; + TPixel32 color1 = m_color1; + + if (cf) { + color0 = (*(cf))(m_color0); + color1 = (*(cf))(m_color1); + } else { + color0 = m_color0; + color1 = m_color1; + } + + UINT i; + + const std::vector &v = outline->getArray(); + + if (v.empty()) + return; + + // outline with antialiasing + glBegin(GL_LINE_STRIP); + tglColor(color0); + for (i = 0; i < v.size(); i += 2) + glVertex2dv(&v[i].x); + glEnd(); + + glBegin(GL_LINE_STRIP); + tglColor(color1); + for (i = 1; i < v.size(); i += 2) + glVertex2dv(&v[i].x); + glEnd(); + + glBegin(GL_QUAD_STRIP); + for (i = 0; i < v.size(); i += 2) { + + tglColor(color0); + glVertex2dv(&v[i].x); + tglColor(color1); + glVertex2dv(&v[i + 1].x); + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +void TBiColorStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + TOutlineUtil::OutlineParameter param; + param.m_lengthStep = tmax(10.0, m_parameter); + TStrokeOutline outline; + TOutlineStyle::computeOutline(stroke, outline, param); + const std::vector &v = outline.getArray(); + if (v.empty()) + return; + + TPixel32 color0 = m_color0; + TPixel32 color1 = m_color1; + flash.setThickness(0.0); + // Just for the polygon grading function + SFlashUtils sfu; + for (UINT i = 0; i < (v.size() - 3); i += 2) { + vector plv; + plv.push_back(TPointD(v[i].x, v[i].y)); + plv.push_back(TPointD(v[i + 2].x, v[i + 2].y)); + plv.push_back(TPointD(v[i + 3].x, v[i + 3].y)); + plv.push_back(TPointD(v[i + 1].x, v[i + 1].y)); + // flash.setFillColor(blend(color0,color1,0.5)); + // flash.drawPolyline(plv); + // graded multipolygons + sfu.drawGradedPolyline(flash, plv, color0, color1); + } +} + +//============================================================================= + +TNormal2StrokeStyle::TNormal2StrokeStyle() + : m_color(TPixel32::Yellow), m_lightx(45.0), m_lighty(200.0), m_shininess(50.0), m_metal(0.5), m_bend(1.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TNormal2StrokeStyle::clone() const +{ + return new TNormal2StrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TNormal2StrokeStyle::getParamCount() const +{ + return 5; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TNormal2StrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TNormal2StrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 5); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TNormal2StrokeStyle", "Light X Pos"); + break; + case 1: + value = QCoreApplication::translate("TNormal2StrokeStyle", "Light Y Pos"); + break; + case 2: + value = QCoreApplication::translate("TNormal2StrokeStyle", "Shininess"); + break; + case 3: + value = QCoreApplication::translate("TNormal2StrokeStyle", "Plastic"); + break; + case 4: + value = QCoreApplication::translate("TNormal2StrokeStyle", "Bump"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + min = -100.0; + max = 100.0; + break; + case 1: + min = -100.0; + max = 100.0; + break; + case 2: + min = 0.1; + max = 128.0; + break; + case 3: + min = 0.0; + max = 1.0; + break; + case 4: + min = 0.0; + max = 1.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TNormal2StrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 5); + double value; + switch (index) { + case 0: + value = m_lightx; + break; + case 1: + value = m_lighty; + break; + case 2: + value = m_shininess; + break; + case 3: + value = m_metal; + break; + case 4: + value = m_bend; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + m_lightx = value; + break; + case 1: + m_lighty = value; + break; + case 2: + m_shininess = value; + break; + case 3: + m_metal = value; + break; + case 4: + m_bend = value; + break; + } + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 121) + throw TException("Normal stroke style: unknown obsolete format"); + + is >> m_color >> m_lightx >> m_lighty >> m_shininess >> m_metal; + m_bend = 1.0; +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const +{ +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, + const TStroke *stroke) const +{ + + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + UINT i; + double bend = 2 * m_bend; + const std::vector &v = outline->getArray(); + if (v.empty()) + return; + vector normal; + + GLfloat light_position[] = {(float)(m_lightx), (float)(m_lighty), 100.0, 0.0}; + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_NORMALIZE); + GLfloat mat_ambient[] = {(float)dcolor.r, (float)dcolor.g, (float)dcolor.b, 1.0}; + GLfloat mat_specular[] = {(float)(m_metal * (1 - dcolor.r) + dcolor.r), + (float)(m_metal * (1 - dcolor.g) + dcolor.g), + (float)(m_metal * (1 - dcolor.b) + dcolor.b), 1.0}; + GLfloat mat_shininess[] = {(float)m_shininess}; + + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); + glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_ambient); + + // glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); + + // outline with antialiasing + glBegin(GL_LINE_STRIP); + for (i = 0; i < v.size(); i += 2) { + T3DPointD pointa(v[i].x, v[i].y, 0); + T3DPointD pointb(v[i + 1].x, v[i + 1].y, 0); + T3DPointD d = pointb - pointa; + if (norm2(d) > 0.0) + d = normalize(d); + normal.push_back(d); + T3DPointD pointaNormal = T3DPointD(0, 0, 1) - bend * d; + tglNormal(pointaNormal); + tglVertex(pointa); + } + glEnd(); + + int normalcounter = 0; + glBegin(GL_LINE_STRIP); + for (i = 1; i < v.size(); i += 2) { + T3DPointD pointa(v[i].x, v[i].y, 0); + T3DPointD pointaNormal = T3DPointD(0, 0, 1) + bend * normal[normalcounter++]; + tglNormal(pointaNormal); + tglVertex(pointa); + } + glEnd(); + + normalcounter = 0; + for (i = 0; i <= v.size() - 4; i += 2) { + glBegin(GL_QUAD_STRIP); + T3DPointD olda(v[i].x, v[i].y, 0); + T3DPointD oldb(v[i + 1].x, v[i + 1].y, 0); + T3DPointD oldcenter = 0.5 * (olda + oldb); + T3DPointD oldcenterNormal(0, 0, 1); + T3DPointD oldaNormal = T3DPointD(0, 0, 1) - bend * normal[normalcounter]; + T3DPointD oldbNormal = T3DPointD(0, 0, 1) + bend * normal[normalcounter]; + T3DPointD a(v[i + 2].x, v[i + 2].y, 0); + T3DPointD b(v[i + 3].x, v[i + 3].y, 0); + T3DPointD center = 0.5 * (a + b); + T3DPointD centerNormal(0, 0, 1); + T3DPointD aNormal = T3DPointD(0, 0, 1) - bend * normal[normalcounter++]; + T3DPointD bNormal = T3DPointD(0, 0, 1) + bend * normal[normalcounter]; + tglNormal(oldaNormal); + tglVertex(olda); + tglNormal(aNormal); + tglVertex(a); + tglNormal(oldcenterNormal); + tglVertex(oldcenter); + tglNormal(centerNormal); + tglVertex(center); + tglNormal(oldbNormal); + tglVertex(oldb); + tglNormal(bNormal); + tglVertex(b); + glEnd(); + } + + glDisable(GL_NORMALIZE); + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); +} + +//----------------------------------------------------------------------------- + +void TNormal2StrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + //double length = stroke->getLength(); + //double step=10.0; + TPointD pos1, pos2, pos3, pos4, oldPos1, oldPos2, oldPos3, oldPos4; + + TOutlineUtil::OutlineParameter param; + param.m_lengthStep = 10.0; + TStrokeOutline outline; + TOutlineStyle::computeOutline(stroke, outline, param); + const std::vector &v = outline.getArray(); + + TPixel32 color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + + TPixel32 color1; + TPixelD dcolor1(0.5 * dcolor.r, 0.5 * dcolor.g, 0.5 * dcolor.b, 1.0); + color1 = toPixel32(dcolor1); + + dcolor = TPixelD(dcolor.r + (1.0 - dcolor.r) * m_metal, + dcolor.g + (1.0 - dcolor.g) * m_metal, + dcolor.b + (1.0 - dcolor.b) * m_metal, dcolor.m); + color = toPixel32(dcolor); + flash.setThickness(0.0); + + SFlashUtils sfu; + for (int i = 0; i <= (int)(v.size() - 4); i += 2) { + TPointD olda(v[i].x, v[i].y); + TPointD oldb(v[i + 1].x, v[i + 1].y); + TPointD oldcenter = 0.5 * (olda + oldb); + TPointD a(v[i + 2].x, v[i + 2].y); + TPointD b(v[i + 3].x, v[i + 3].y); + TPointD center = 0.5 * (a + b); + + vector vpl; + vpl.push_back(olda); + vpl.push_back(a); + vpl.push_back(center); + vpl.push_back(oldcenter); + sfu.drawGradedPolyline(flash, vpl, color1, color); + + vpl.clear(); + vpl.push_back(oldb); + vpl.push_back(b); + vpl.push_back(center); + vpl.push_back(oldcenter); + sfu.drawGradedPolyline(flash, vpl, color1, color); + } +} + +//============================================================================= + +namespace +{ +double get_inout_intensityslope(double in, double out, double t) +{ + if (out < in) + out = in; + if (t < in) + return t / in; + else if (t > out) + return (t - 1) / (out - 1); + else + return 1; +} +} + +//----------------------------------------------------------------------------- + +TChalkStrokeStyle2::TChalkStrokeStyle2() + : m_color(TPixel32::Black), m_blend(1.0), m_intensity(50.0), m_in(0.25), m_out(0.25), m_noise(0.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TChalkStrokeStyle2::clone() const +{ + return new TChalkStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +int TChalkStrokeStyle2::getParamCount() const +{ + return 5; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TChalkStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TChalkStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 5); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TChalkStrokeStyle2", "Border Fade"); + break; + case 1: + value = QCoreApplication::translate("TChalkStrokeStyle2", "Density"); + break; + case 2: + value = QCoreApplication::translate("TChalkStrokeStyle2", "Fade In"); + break; + case 3: + value = QCoreApplication::translate("TChalkStrokeStyle2", "Fade Out"); + break; + case 4: + value = QCoreApplication::translate("TChalkStrokeStyle2", "Noise"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TChalkStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + min = 0.0; + max = 1.0; + break; + case 1: + min = 0.0; + max = 100.0; + break; + case 2: + min = 0.0; + max = 1.0; + break; + case 3: + min = 0.0; + max = 1.0; + break; + case 4: + min = 0.0; + max = 1.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TChalkStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 5); + double value = 0; + switch (index) { + case 0: + value = m_blend; + break; + case 1: + value = m_intensity; + break; + case 2: + value = m_in; + break; + case 3: + value = m_out; + break; + case 4: + value = m_noise; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TChalkStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + m_blend = value; + break; + case 1: + m_intensity = value; + break; + case 2: + m_in = value; + break; + case 3: + m_out = value; + break; + case 4: + m_noise = value; + break; + } + + //updateVersionNumber(); non serve perche' i parametri vengono sfrutttati direttamente nella draw +} + +//----------------------------------------------------------------------------- + +void TChalkStrokeStyle2::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 105) + throw TException("chalk stroke style: unknown obsolete format"); + m_in = 0.0, m_out = 0.0, m_noise = 0.0; + + is >> m_color >> m_blend >> m_intensity; + m_blend = 1 - m_blend; +} + +//----------------------------------------------------------------------------- + +void TChalkStrokeStyle2::computeData(Doubles &data, const TStroke *stroke, const TColorFunction *cf) const +{ + double length = stroke->getLength(); + double step = 4; + double s = 0; + + data.clear(); + data.reserve(tceil(length / step) * 6 + 6); + + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + + data.push_back(pos.x); + data.push_back(pos.y); + data.push_back(pos.thick); + data.push_back(u.x); + data.push_back(u.y); + data.push_back(s / length); + + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TChalkStrokeStyle2::drawStroke(const TColorFunction *cf, Doubles &data, const TStroke *stroke) const +{ + double step = 4; + + double blend = m_blend; //distanza che controlla da dove il gessetto comincia il fade out (0, 1) + double intensitymax = m_intensity; //quanti punti vengono disegnati ad ogni step + + TRandom rnd; + TRandom rnd_noise; + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + double mattedcolor = 0.5 * dcolor.m; + + double noise = 0; + double noiseslope = 0; + double tmpnoise = 0; + + GLuint chalkId; + chalkId = glGenLists(1); + glNewList(chalkId, GL_COMPILE); + glBegin(GL_QUADS); + glVertex2d(1, 1); + glVertex2d(-1, 1); + glVertex2d(-1, -1); + glVertex2d(1, -1); + glEnd(); + glEndList(); + + for (UINT i = 0; i < data.size(); i += 6) { + TThickPoint pos; + pos.x = data[i]; + pos.y = data[i + 1]; + pos.thick = data[i + 2]; + TPointD u; + u.x = data[i + 3]; + u.y = data[i + 4]; + TPointD v = rotate90(u); + + TPointD shift; + double intslope = get_inout_intensityslope(m_in, 1 - m_out, data[i + 5]); + double transpslope = (intslope / blend) * dcolor.m; + if (m_noise) { + if (tmpnoise <= 0) { + noise = (100 / m_noise) * rnd_noise.getFloat(); + tmpnoise = noise; + } + noiseslope = get_inout_intensityslope(0.5, 0.5, tmpnoise / noise); + tmpnoise -= step; + } else + noiseslope = 1; + + for (int i = 0; i < intensitymax * intslope * noiseslope; i++) { + double vrandnorm = rnd.getFloat(-1.0, 1.0); + double randomv = vrandnorm * pos.thick * noiseslope; + double randomu = (0.5 - rnd.getFloat()) * step; + shift = pos + u * randomu + v * randomv; + double mod = fabs(vrandnorm); + if (mod > 1 - blend) + glColor4d(dcolor.r, dcolor.g, dcolor.b, (double)rnd.getFloat() * ((1 - mod) * transpslope)); + else + glColor4d(dcolor.r, dcolor.g, dcolor.b, mattedcolor); + glPushMatrix(); + glTranslated(shift.x, shift.y, 0.0); + glCallList(chalkId); + glPopMatrix(); + } + } + + glDeleteLists(chalkId, 1); +} + +//============================================================================= + +TBlendStrokeStyle2::TBlendStrokeStyle2() + : m_color(TPixel32::Red), m_blend(1.0), m_in(0.25), m_out(0.25) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TBlendStrokeStyle2::clone() const +{ + return new TBlendStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +int TBlendStrokeStyle2::getParamCount() const +{ + return 3; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TBlendStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TBlendStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 3); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TBlendStrokeStyle2", "Border Fade"); + break; + case 1: + value = QCoreApplication::translate("TBlendStrokeStyle2", "Fade In"); + break; + case 2: + value = QCoreApplication::translate("TBlendStrokeStyle2", "Fade Out"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 3); + min = 0.0; + max = 1.0; +} + +//----------------------------------------------------------------------------- + +double TBlendStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 3); + double value = 0; + switch (index) { + case 0: + value = m_blend; + break; + case 1: + value = m_in; + break; + case 2: + value = m_out; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 3); + switch (index) { + case 0: + m_blend = value; + break; + case 1: + m_in = value; + break; + case 2: + m_out = value; + break; + } + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 110) + throw TException("Blend stroke style: unknown obsolete format"); + m_in = 0.0, m_out = 0.0; + is >> m_color >> m_blend; + m_blend = 1 - m_blend; +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::computeData(PointsAndDoubles &data, + const TStroke *stroke, + const TColorFunction *cf) const +{ + data.clear(); + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + double step = 10.0; + TPointD pos1, pos2, pos3, pos4, oldPos1, oldPos2, oldPos3, oldPos4; + double oldintslope; + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + bool firstRing = true; + double s = 0; + double maxfactor = 2 * m_blend / step; //max definisce il numero di intervalli in cui la regione viene divisa + //per evitare il problema del blend poco efficiente sui triangoli + double minthickness = MINTHICK * sqrt(tglGetPixelSize2()); + double thickness = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + if (pos.thick < MINTHICK) + thickness = minthickness; + else + thickness = pos.thick; + TPointD v = rotate90(u) * thickness; + + TPointD v1 = v * (1 - m_blend); + pos1 = pos + v; + pos2 = pos + v1; + pos3 = pos - v1; + pos4 = pos - v; + double intslope = get_inout_intensityslope(m_in, 1 - m_out, s / length) * dcolor.m; + if (firstRing) { + firstRing = false; + } else { + int max = (int)(maxfactor * thickness); + double invmax = 1.0 / max; + data.push_back(make_pair(oldPos1, 0.0)); + data.push_back(make_pair(pos1, 0.0)); + int i; + for (i = 1; i < max; i++) { + data.push_back(make_pair(i * (oldPos2 - oldPos1) * invmax + oldPos1, (i * oldintslope) * invmax)); + data.push_back(make_pair(i * (pos2 - pos1) * invmax + pos1, (i * intslope) * invmax)); + } + + data.push_back(make_pair(oldPos2, oldintslope)); + data.push_back(make_pair(pos2, intslope)); + data.push_back(make_pair(oldPos3, oldintslope)); + data.push_back(make_pair(pos3, intslope)); + + for (i = 0; i < max; i++) { + data.push_back(make_pair(i * (oldPos4 - oldPos3) * invmax + oldPos3, (oldintslope * invmax) * (max - i))); + data.push_back(make_pair(i * (pos4 - pos3) * invmax + pos3, (intslope * invmax) * (max - i))); + } + + data.push_back(make_pair(oldPos4, 0.0)); + data.push_back(make_pair(pos4, 0.0)); + } + oldPos1 = pos1; + oldPos2 = pos2; + oldPos3 = pos3; + oldPos4 = pos4; + oldintslope = intslope; + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::drawStroke(const TColorFunction *cf, + PointsAndDoubles &data, + const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + TPixelD dcolor; + dcolor = toPixelD(color); + + PointsAndDoubles::iterator it = data.begin(); + glBegin(GL_QUAD_STRIP); + for (; it != data.end(); ++it) { + glColor4d(dcolor.r, dcolor.g, dcolor.b, it->second); + tglVertex(it->first); + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +void TBlendStrokeStyle2::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + double step = 10.0; + TPointD pos1, pos2, pos3, pos4, oldPos1, oldPos2, oldPos3, oldPos4; + double oldintslope; + TPixel32 color = m_color; + + double lblend = m_blend; + // For the Flash version, to simplify the grading. + lblend = 1.0; + + TPixelD dcolor; + dcolor = toPixelD(color); + bool firstRing = true; + double s = 0; + //double maxfactor=2*lblend/step; //max definisce il numero di intervalli in cui la regione viene divisa + //per evitare il problema del blend poco efficiente sui triangoli + + vector vp1, vp2; + vector vdc1, vdc2; + + flash.setThickness(0); + SFlashUtils sfu; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * pos.thick; + TPointD v1 = v * (1 - lblend); + pos1 = pos + v; + pos2 = pos + v1; + pos3 = pos - v1; + pos4 = pos - v; + double intslope = get_inout_intensityslope(m_in, 1 - m_out, s / length) * dcolor.m; + if (firstRing) { + firstRing = false; + } else { + vp1.clear(); + vp2.clear(); + vdc1.clear(); + vdc2.clear(); + + // The Flash version has been simplified. Only one direction grading! + + vdc1.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, 0)); + vp1.push_back(oldPos1); + vdc2.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, 0)); + vp2.push_back(pos1); + vdc1.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, oldintslope)); + vp1.push_back(oldPos2); + vdc2.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, intslope)); + vp2.push_back(pos2); + vdc1.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, oldintslope)); + vp1.push_back(oldPos3); + vdc2.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, intslope)); + vp2.push_back(pos3); + vdc1.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, 0)); + vp1.push_back(oldPos4); + vdc2.push_back(TPixelD(dcolor.r, dcolor.g, dcolor.b, 0)); + vp2.push_back(pos4); + + vector vpl; + vpl.push_back(vp1[0]); + vpl.push_back(vp1[3]); + vpl.push_back(vp2[3]); + vpl.push_back(vp2[0]); + + TPixel32 col[4]; + col[0] = toPixel32(vdc1[1]); + col[1] = toPixel32(vdc1[2]); + col[2] = toPixel32(vdc2[2]); + col[3] = toPixel32(vdc2[1]); + + sfu.drawGradedPolyline(flash, vpl, blend(col[0], col[1], 0.5), blend(col[2], col[3], 0.5)); + } + oldPos1 = pos1; + oldPos2 = pos2; + oldPos3 = pos3; + oldPos4 = pos4; + oldintslope = intslope; + s += step; + } +} + +//============================================================================= + +TTwirlStrokeStyle::TTwirlStrokeStyle() + : m_color(TPixel32::Green), m_period(30.0), m_blend(0.5) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TTwirlStrokeStyle::clone() const +{ + return new TTwirlStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TTwirlStrokeStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TTwirlStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TTwirlStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? QCoreApplication::translate("TTwirlStrokeStyle", "Twirl") : QCoreApplication::translate("TTwirlStrokeStyle", "Shade"); +} + +//----------------------------------------------------------------------------- + +void TTwirlStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 1.0; + max = 100.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TTwirlStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? m_period : m_blend; +} + +//----------------------------------------------------------------------------- + +void TTwirlStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + if (index == 0) + m_period = value; + else + m_blend = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TTwirlStrokeStyle::computeData(Doubles &data, const TStroke *stroke, const TColorFunction *cf) const +{ + double length = stroke->getLength(); + double step = 5.0; + double period = 10 * (102 - m_period); + double hperiod = period / 2; + double blendval = 0; + TRandom rnd; + + data.clear(); + data.reserve(tceil(length / step) + 1); + + double s = 0; + TPointD app; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * (pos.thick); + double shift = sin((TConsts::pi / hperiod) * s); + + app = pos + v * shift; + data.push_back(app.x); + data.push_back(app.y); + app = pos - v * shift; + data.push_back(app.x); + data.push_back(app.y); + + blendval = get_inout_intensityslope(m_blend, 1.0 - m_blend, (s - ((int)(s / hperiod) * hperiod)) / hperiod); + data.push_back(blendval); + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TTwirlStrokeStyle::drawStroke(const TColorFunction *cf, Doubles &data, const TStroke *stroke) const +{ + TPixel32 blackcolor = TPixel32::Black; + + TPixel32 color; + if (cf) { + color = (*(cf))(m_color); + blackcolor = (*(cf))(blackcolor); + } else { + color = m_color; + } + blackcolor.m = m_color.m; + + TPointD app; + UINT i = 0; + + glBegin(GL_QUAD_STRIP); + for (; i < data.size(); i += 5) { + tglColor(blend(blackcolor, color, data[i + 4])); + app.x = data[i]; + app.y = data[i + 1]; + tglVertex(app); + app.x = data[i + 2]; + app.y = data[i + 3]; + tglVertex(app); + } + glEnd(); + + for (i = 5; i < data.size(); i += 5) { + tglColor(blend(color, blackcolor, data[i + 4])); + glBegin(GL_LINES); + app.x = data[i - 5]; + app.y = data[i - 4]; + tglVertex(app); + app.x = data[i]; + app.y = data[i + 1]; + tglVertex(app); + glEnd(); + glBegin(GL_LINES); + app.x = data[i - 3]; + app.y = data[i - 2]; + tglVertex(app); + app.x = data[i + 2]; + app.y = data[i + 3]; + tglVertex(app); + glEnd(); + } +} + +//----------------------------------------------------------------------------- + +void TTwirlStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + + double length = stroke->getLength(); + double step = 5.0; + double period = 10 * (102 - m_period); + double hperiod = period / 2; + double blendval = 0; + TRandom rnd; + TPixel32 blackcolor = TPixel32::Black; + TPixel32 color = m_color; + blackcolor.m = m_color.m; + + vector points1; + vector points2; + vector vblend; + double s = 0; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u) * (pos.thick); + double shift = sin((TConsts::pi / hperiod) * s); + points1.push_back(pos + v * shift); + points2.push_back(pos - v * shift); + blendval = get_inout_intensityslope(m_blend, 1.0 - m_blend, (s - ((int)(s / hperiod) * hperiod)) / hperiod); + vblend.push_back(blendval); + s += step; + } + + SFlashUtils sfu; + for (int i = 1; i < (int)points1.size(); i++) { + vector vp; + vp.push_back(points1[i - 1]); + vp.push_back(points2[i - 1]); + vp.push_back(points2[i]); + vp.push_back(points1[i]); + flash.setThickness(0.0); + sfu.drawGradedPolyline(flash, vp, blend(blackcolor, color, vblend[i - 1]), + blend(blackcolor, color, vblend[i])); + // flash.setFillColor(blend(blackcolor, color , vblend[i-1])); + // flash.drawPolyline(vp); + + vector sv; + sv.push_back(TSegment(points1[i - 1], points1[i])); + sv.push_back(TSegment(points2[i - 1], points2[i])); + flash.setThickness(1.0); + flash.setLineColor(blend(color, blackcolor, vblend[i - 1])); + flash.drawSegments(sv, false); + } +} + +//============================================================================= + +TSawToothStrokeStyle::TSawToothStrokeStyle(TPixel32 color, double parameter) + : m_color(color), m_parameter(parameter) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TSawToothStrokeStyle::clone() const +{ + return new TSawToothStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +void TSawToothStrokeStyle::computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const +{ + param.m_lengthStep = m_parameter; + TOutlineStyle::computeOutline(stroke, outline, param); +} + +//----------------------------------------------------------------------------- + +void TSawToothStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + TOutlineUtil::OutlineParameter param; + param.m_lengthStep = tmax(20.0, m_parameter); + TStrokeOutline outline; + TOutlineStyle::computeOutline(stroke, outline, param); + const std::vector &v = outline.getArray(); + if (v.empty()) + return; + + TPixel32 color = m_color; + flash.setThickness(0.0); + flash.setFillColor(color); + + if (v.empty()) + return; + double old[2]; + int counter = 0; + for (UINT i = 0; i < v.size() - 2; i += 2) { + if (0 != v[i].stepCount) { + if (counter) { + vector plv; + // flash.setFillColor(color); + + plv.push_back(TPointD(old[0], old[1])); + plv.push_back(TPointD(v[i].x, v[i].y)); + plv.push_back(TPointD(v[i + 1].x, v[i + 1].y)); + flash.drawPolyline(plv); + } + old[0] = v[i].x; + old[1] = v[i].y; + counter++; + } + } +} + +//----------------------------------------------------------------------------- + +void TSawToothStrokeStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, + const TStroke *stroke) const +{ + UINT i; + int counter = 0; + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + tglColor(color); + const std::vector &v = outline->getArray(); + if (v.empty()) + return; + double old[2]; + // outline with antialiasing + glBegin(GL_LINE_STRIP); + for (i = 0; i < v.size() - 2; i += 2) { + if (0 != v[i].stepCount) { + if (counter) { + glVertex2dv(old); + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + glVertex2dv(old); + } + old[0] = v[i].x; + old[1] = v[i].y; + counter++; + } + } + glEnd(); + counter = 0; + glBegin(GL_TRIANGLES); + for (i = 0; i < v.size() - 2; i += 2) { + if (0 != v[i].stepCount) { + if (counter) { + glVertex2dv(old); + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + } + old[0] = v[i].x; + old[1] = v[i].y; + counter++; + } + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +int TSawToothStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TSawToothStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TSawToothStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TSawToothStrokeStyle", "Distance"); +} + +//----------------------------------------------------------------------------- + +void TSawToothStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 0.1; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TSawToothStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_parameter; +} + +//----------------------------------------------------------------------------- + +void TSawToothStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_parameter = value; + updateVersionNumber(); //questo si chiama per ogno cambiamento di parametro per cui di deve ricalcolare l'outline +} + +//============================================================================= + +TMultiLineStrokeStyle2::TMultiLineStrokeStyle2() + : m_color0(TPixel32(0, 255, 0)), m_color1(TPixel32(0, 0, 0)), m_intensity(0.2), m_length(20.0), m_thick(0.3), m_noise(0.5) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TMultiLineStrokeStyle2::clone() const +{ + return new TMultiLineStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +namespace +{ + +typedef struct { + TPointD u, v; + TThickPoint p; +} myLineData; +} + +//----------------------------------------------------------------------------- + +int TMultiLineStrokeStyle2::getParamCount() const +{ + return 4; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TMultiLineStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TMultiLineStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 4); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TMultiLineStrokeStyle2", "Density"); + break; + case 1: + value = QCoreApplication::translate("TMultiLineStrokeStyle2", "Size"); + break; + case 2: + value = QCoreApplication::translate("TMultiLineStrokeStyle2", "Thickness"); + break; + case 3: + value = QCoreApplication::translate("TMultiLineStrokeStyle2", "Noise"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = 0.0; + max = 1.0; + break; + case 1: + min = 0.0; + max = 100.0; + break; + case 2: + min = 0.0; + max = 1.0; + break; + case 3: + min = 0.0; + max = 1.0; + break; + } +} +//----------------------------------------------------------------------------- + +double TMultiLineStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + double value = 0; + switch (index) { + case 0: + value = m_intensity; + break; + case 1: + value = m_length; + break; + case 2: + value = m_thick; + break; + case 3: + value = m_noise; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + m_intensity = value; + break; + case 1: + m_length = value; + break; + case 2: + m_thick = value; + break; + case 3: + m_noise = value; + break; + } + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 118 && ids != 128) + throw TException("Multi Line stroke style: unknown obsolete format"); + if (ids == 118) { + m_length = 20.0, m_thick = 0.3, m_noise = 0.0; + is >> m_color0 >> m_intensity; + m_color1 = TPixel32::Black; + } else { + is >> m_color0 >> m_intensity >> m_length >> m_thick >> m_noise; + m_color1 = TPixel32::Black; + } +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::computeData(BlendAndPoints &data, const TStroke *stroke, const TColorFunction *cf) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + double step = 4.0; + int maxlength = (int)m_length; + double factor = 0; + TRandom rnd; + + vector LineData; + myLineData Data; + double s = 0; + + double minthickness = MINTHICK * sqrt(tglGetPixelSize2()); + double thickness = 0; + double strokethick = m_thick; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + Data.p = stroke->getThickPoint(w); + Data.u = stroke->getSpeed(w); + if (norm2(Data.u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + Data.u = normalize(Data.u); + if (Data.p.thick < MINTHICK) + thickness = minthickness; + else + thickness = Data.p.thick; + Data.v = rotate90(Data.u) * thickness; + + LineData.push_back(Data); + s += step; + } + + BlendAndPoint appData; + + data.clear(); + data.reserve(LineData.size()); + + for (int i = 0; i < m_intensity * LineData.size(); i++) { + appData.points.clear(); + int start = rnd.getInt(0, LineData.size()); + int end = start + maxlength + rnd.getInt(0, maxlength); + if (end > (int)LineData.size()) + end = LineData.size(); + double halfcount = (end - start) / 2.0; + double vshift = (0.5 - rnd.getFloat()); + + appData.blend = rnd.getFloat(); + + for (int j = 0; j < (end - start); j++) { + if (j < halfcount) + factor = j / halfcount; + else + factor = 1 - (j - halfcount) / halfcount; + appData.points.push_back(LineData[j + start].p + LineData[j + start].v * (vshift - strokethick * factor * (1 - m_noise * (1 - rnd.getFloat())))); + appData.points.push_back(LineData[j + start].p + LineData[j + start].v * (vshift + strokethick * factor * (1 - m_noise * (1 - rnd.getFloat())))); + } + data.push_back(appData); + } +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::drawStroke(const TColorFunction *cf, BlendAndPoints &data, const TStroke *stroke) const +{ + TPixel32 color0, color1; + if (cf) { + color0 = (*(cf))(m_color0); + color1 = (*(cf))(m_color1); + } else { + color0 = m_color0; + color1 = m_color1; + } + + glEnable(GL_POLYGON_SMOOTH); + + for (UINT i = 0; i < data.size(); i++) { + tglColor(blend(color0, color1, data[i].blend)); + + glBegin(GL_QUAD_STRIP); + for (UINT j = 0; j < data[i].points.size(); j++) + tglVertex(data[i].points[j]); + + glEnd(); + } + glDisable(GL_POLYGON_SMOOTH); +} + +//----------------------------------------------------------------------------- + +void TMultiLineStrokeStyle2::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + double step = 4.0; + int maxlength = (int)m_length; + double factor = 0; + TRandom rnd; + TPixel32 color0, color1; + color0 = m_color0; + color1 = m_color1; + + vector LineData; + myLineData Data; + double s = 0; + double strokethick = m_thick; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < 0) { + s += 0.1; + continue; + } // per tamponare il baco della getParameterAtLength() + Data.p = stroke->getThickPoint(w); + Data.u = stroke->getSpeed(w); + if (norm2(Data.u) == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + Data.u = normalize(Data.u); + Data.v = rotate90(Data.u) * Data.p.thick; + LineData.push_back(Data); + s += step; + } + + for (int i = 0; i < m_intensity * LineData.size(); i++) { + int start = rnd.getInt(0, LineData.size()); + int end = start + maxlength + rnd.getInt(0, maxlength); + if (end > (int)LineData.size()) + end = LineData.size(); + double halfcount = (end - start) / 2.0; + double vshift = (0.5 - rnd.getFloat()); + flash.setThickness(0.0); + flash.setFillColor(blend(color0, color1, rnd.getFloat())); + vector sv; + int j; + for (j = 0; j < (end - start); j++) { + if (j < halfcount) + factor = j / halfcount; + else + factor = 1 - (j - halfcount) / halfcount; + float rand = rnd.getFloat(); + TPointD p0 = (LineData[j + start].p + LineData[j + start].v * (vshift - strokethick * factor * (1 - m_noise * (1 - rand)))); + TPointD p1 = (LineData[j + start].p + LineData[j + start].v * (vshift + strokethick * factor * (1 - m_noise * (1 - rand)))); + sv.push_back(TSegment(p0, p1)); + } + for (j = 0; j < ((int)sv.size() - 1); j++) { + vector pv; + pv.push_back(sv[j].getP0()); + pv.push_back(sv[j].getP1()); + pv.push_back(sv[j + 1].getP1()); + pv.push_back(sv[j + 1].getP0()); + flash.drawPolyline(pv); + } + } +} + +//============================================================================= + +TZigzagStrokeStyle::TZigzagStrokeStyle() + : m_color(TPixel32(0, 0, 0)), m_minDist(0.5), m_maxDist(6.0), m_minAngle(30.0), m_maxAngle(60.0), m_thickness(1.0) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TZigzagStrokeStyle::clone() const +{ + return new TZigzagStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TZigzagStrokeStyle::getParamCount() const +{ + return 5; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TZigzagStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TZigzagStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 5); + QString value; + switch (index) { + case 0: + value = QCoreApplication::translate("TZigzagStrokeStyle", "Min Distance"); + break; + case 1: + value = QCoreApplication::translate("TZigzagStrokeStyle", "Max Distance"); + break; + case 2: + value = QCoreApplication::translate("TZigzagStrokeStyle", "Min Angle"); + break; + case 3: + value = QCoreApplication::translate("TZigzagStrokeStyle", "Max Angle"); + break; + case 4: + value = QCoreApplication::translate("TZigzagStrokeStyle", "Thickness"); + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + min = 0.5; + max = 50.0; + break; + case 1: + min = 0.5; + max = 50.0; + break; + case 2: + min = -90.0; + max = 90.0; + break; + case 3: + min = -90.0; + max = 90.0; + break; + case 4: + min = 0.0; + max = 3.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double TZigzagStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 5); + double value = 0; + switch (index) { + case 0: + value = m_minDist; + break; + case 1: + value = m_maxDist; + break; + case 2: + value = m_minAngle; + break; + case 3: + value = m_maxAngle; + break; + case 4: + value = m_thickness; + break; + } + return value; +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 5); + switch (index) { + case 0: + m_minDist = value; + break; + case 1: + m_maxDist = value; + break; + case 2: + m_minAngle = value; + break; + case 3: + m_maxAngle = value; + break; + case 4: + m_thickness = value; + break; + } + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +bool TZigzagStrokeStyle::getZigZagPosition(const TStroke *stroke, TRandom &rnd, + const double s, const int first, + const double minTranslLength, + TThickPoint &pos, TThickPoint &pos1) const +{ + double w = stroke->getParameterAtLength(s); + pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) < TConsts::epsilon) + return false; + u = normalize(u); + TPointD uu = u; + double angle = m_minAngle + (m_maxAngle - m_minAngle) * (double)rnd.getUInt(101) * 0.01; + TRotation rotM(angle); + u = rotM * u; + double maxTranslLength = angle == 90 ? 1.0 : 2.0; + if (angle > 30 && angle < 90) { + double rta = 1.0 / tan(degree2rad(angle)); + maxTranslLength = sqrt(sq(rta) + 1.0); + } + double r = (minTranslLength + (maxTranslLength - minTranslLength) * rnd.getFloat()) * pos.thick * first; + pos = pos + r * u; + pos1 = pos + uu * m_thickness; + return true; +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::setRealMinMax() const +{ + TZigzagStrokeStyle *ncpthis = const_cast(this); + double minDist = tmin(m_minDist, m_maxDist); + double maxDist = tmax(m_minDist, m_maxDist); + double minAngle = tmin(m_minAngle, m_maxAngle); + double maxAngle = tmax(m_minAngle, m_maxAngle); + ncpthis->m_minDist = minDist; + ncpthis->m_maxDist = maxDist; + ncpthis->m_minAngle = minAngle; + ncpthis->m_maxAngle = maxAngle; +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::computeData(Points &points, const TStroke *stroke, const TColorFunction *cf) const +{ + assert(glGetError() == GL_NO_ERROR); + assert(!!stroke); + double length = stroke->getLength(); + if (length <= 0) + return; + + setRealMinMax(); + // e.g minimum translation length is the half of the thickness + const double minTranslLength = 0.7; + //const bool isTransparent=m_color.m<255; + + int first = 1; + TThickPoint pos; + TThickPoint pos1; + TRandom rnd; + + points.clear(); + points.reserve(tceil(length / m_minDist) * 2 + 2); + + for (double s = 0.0; s <= length; first = -first) { + if (getZigZagPosition(stroke, rnd, s, first, minTranslLength, pos, pos1)) { + //TRectD rec(pos.x,pos.y,pos1.x,pos1.y); + points.push_back(pos); + points.push_back(pos1); + } + s += m_minDist + (m_maxDist - m_minDist) * (double)rnd.getUInt(101) * 0.01; + } + if (getZigZagPosition(stroke, rnd, length - TConsts::epsilon, first, minTranslLength, pos, pos1)) { + points.push_back(pos); + points.push_back(pos1); + } +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::drawStroke(const TColorFunction *cf, Points &points, const TStroke *stroke) const +{ + if (points.size() <= 1) + return; + TPixel32 color; + + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + tglColor(m_color); + + glEnableClientState(GL_VERTEX_ARRAY); + + glVertexPointer(2, GL_DOUBLE, sizeof(TPointD), &points[0]); + glDrawArrays(GL_QUAD_STRIP, 0, points.size()); + /* + glBegin(GL_QUAD_STRIP); + for(UINT i=0; igetLength(); + if (length <= 0) + return; + + setRealMinMax(); + // e.g minimum translation length is the half of the thickness + const double minTranslLength = 0.7; + + int first = 1; + TThickPoint pos; + TThickPoint pos1; + TRandom rnd; + RectVector rects; + + for (double s = 0.0; s <= length; first = -first) { + if (getZigZagPosition(stroke, rnd, s, first, minTranslLength, pos, pos1)) { + TRectD rec(pos.x, pos.y, pos1.x, pos1.y); + rects.push_back(rec); + } + s += m_minDist + (m_maxDist - m_minDist) * (double)rnd.getUInt(101) * 0.01; + } + if (getZigZagPosition(stroke, rnd, length - TConsts::epsilon, first, minTranslLength, pos, pos1)) { + TRectD rec(pos.x, pos.y, pos1.x, pos1.y); + rects.push_back(rec); + } + + flash.setLineColor(m_color); + vector segmentsArray; + + flash.setThickness(m_thickness); + RectVector::const_iterator rvi = rects.begin(); + for (; rvi != (rects.end() - 1); rvi++) { + RectVector::const_iterator rvii = rvi + 1; + TPointD p0((rvi->x0 + rvi->x1) / 2.0, (rvi->y0 + rvi->y1) / 2.0); + TPointD p1((rvii->x0 + rvii->x1) / 2.0, (rvii->y0 + rvii->y1) / 2.0); + segmentsArray.push_back(TSegment(p0, p1)); + } + + flash.drawSegments(segmentsArray, false); +} + +//============================================================================= + +TSinStrokeStyle::TSinStrokeStyle() + : m_color(TPixel32::Black), m_frequency(10.0), m_thick(0.4) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TSinStrokeStyle::clone() const +{ + return new TSinStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int TSinStrokeStyle::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TSinStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TSinStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? QCoreApplication::translate("TSinStrokeStyle", "Frequency") : QCoreApplication::translate("TZigzTSinStrokeStyleagStrokeStyle", "Thickness"); +} + +//----------------------------------------------------------------------------- + +void TSinStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = 1.0; + max = 20.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TSinStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + return index == 0 ? m_frequency : m_thick; +} + +//----------------------------------------------------------------------------- + +void TSinStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + if (index == 0) + m_frequency = value; + else + m_thick = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TSinStrokeStyle::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + double length = stroke->getLength(); + double step = 5.0; + + positions.clear(); + positions.reserve(tceil((length + 1) / step)); + + double frequency = m_frequency / 100; + ; + double s = 0; + //bool firstRing = true; + double thick = 1 - m_thick; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + double sinvalue = sin(frequency * s); + positions.push_back(pos + v * pos.thick * sinvalue); + positions.push_back(pos + v * thick * pos.thick * sinvalue); + s += step; + } +} + +//----------------------------------------------------------------------------- + +void TSinStrokeStyle::drawStroke(const TColorFunction *cf, std::vector &positions, + const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + tglColor(color); + + glBegin(GL_QUAD_STRIP); + int i = 0; + for (; i < (int)positions.size(); i += 2) { + tglVertex(positions[i]); + tglVertex(positions[i + 1]); + } + glEnd(); + glBegin(GL_LINE_STRIP); + for (i = 0; i < (int)positions.size(); i += 2) { + tglVertex(positions[i]); + } + glEnd(); + glBegin(GL_LINE_STRIP); + for (i = 1; i < (int)positions.size() - 1; i += 2) { + tglVertex(positions[i]); + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +void TSinStrokeStyle::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + + double length = stroke->getLength(); + double step = 5.0; + + double frequency = m_frequency / 100; + ; + vector points; + + double s = 0; + //bool firstRing = true; + double thick = 1 - m_thick; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + //if(w<0) {s+=0.1; continue;} // per tamponare il baco della getParameterAtLength() + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + double normu = norm2(u); + if (normu == 0) { + s += 0.1; + continue; + } // non dovrebbe succedere mai, ma per prudenza.... + u = normalize(u); + TPointD v = rotate90(u); + double sinvalue = sin(frequency * s); + points.push_back(pos + v * pos.thick * sinvalue); + points.push_back(pos + v * thick * pos.thick * sinvalue); + s += step; + } + + // Polyline version + flash.setThickness(0.0); + flash.setFillColor(m_color); + for (int i = 0; i < ((int)points.size() - 2); i += 2) { + vector plv; + plv.push_back(points[i]); + plv.push_back(points[i + 1]); + plv.push_back(points[i + 3]); + plv.push_back(points[i + 2]); + flash.drawPolyline(plv); + } + + // Quadratic version + /* flash.setThickness(m_thick); + vector pp; + for( int i=0; i<(int)(points.size()-1); i+=2 ) + pp.push_back(TPointD((points[i]+points[i+1])*0.5)); + + if ( pp.size()<=2 ) { + if ( pp.size()==2 ) { + vector sv; + sv.push_back(TSegment(pp[0],pp[1])); + flash.setLineColor(m_color); + flash.drawSegments(sv,false); + } + return; + } + + vector qv; + qv.push_back(TQuadratic(pp[i],pp[i]*0.75+pp[i+1]*0.25,(pp[i]+pp[i+1])*0.5)); + for( i=1; i<(int)(pp.size()-1); i++ ) { + TPointD p0=((pp[i-1]+pp[i])*0.5); + TPointD p1=pp[i]; + TPointD p2=((pp[i]+pp[i+1])*0.5); + qv.push_back(TQuadratic(p0,p1,p2)); + } + int n=pp.size()-1; + qv.push_back(TQuadratic((pp[n-1]+pp[n])*0.5,pp[n-1]*0.25+pp[n]*0.75,pp[n])); + + flash.setLineColor(m_color); + flash.setThickness(m_thick); + flash.drawquads(qv); +*/ +} + +//============================================================================= + +TFriezeStrokeStyle2::TFriezeStrokeStyle2() + : m_color(TPixel32::Black), m_parameter(0.7), m_thick(0.3) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TFriezeStrokeStyle2::clone() const +{ + return new TFriezeStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +int TFriezeStrokeStyle2::getParamCount() const +{ + return 2; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TFriezeStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TFriezeStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 2); + if (index == 0) + return QCoreApplication::translate("TFriezeStrokeStyle2", "Twirl"); + else + return QCoreApplication::translate("TFriezeStrokeStyle2", "Thickness"); +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 2); + if (index == 0) { + min = -1.0; + max = 1.0; + } else { + min = 0.0; + max = 1.0; + } +} + +//----------------------------------------------------------------------------- + +double TFriezeStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 2); + if (index == 0) + return m_parameter; + else + return m_thick; +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 2); + if (index == 0) + m_parameter = value; + else + m_thick = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::loadData(int ids, TInputStreamInterface &is) +{ + if (ids != 102) + throw TException("Frieze stroke style: unknown obsolete format"); + m_thick = 0.0; + is >> m_color >> m_parameter; +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::computeData(Points &positions, + const TStroke *stroke, + const TColorFunction *cf) const +{ + using TConsts::pi; + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + double ds = 0.5; + + positions.clear(); + positions.reserve(tceil((length + 1) / ds)); + + double s = 0.01; + double lastS = 0; + double phi = 0; + double lastW = 0; + double thick = 1 - m_thick; + vector points; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < lastW) { + s += 0.1; + continue; + } + lastW = w; + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = normalize(stroke->getSpeed(w)); + TPointD v = rotate90(u); + + double thickness = pos.thick; // 5; //(1-t)*40 + t * 10; + + if (thickness > 0) { + double omega = pi * 2 / (thickness * 2); + + double q = 0.5 * (1 - cos(phi)); + double theta = pi * 0.5 - pi * m_parameter * q; + double r = thickness * sin(phi); + double r1 = r * thick; + double costheta = cos(theta); + double sintheta = sin(theta); + positions.push_back(pos + u * (r * costheta) + v * (r * sintheta)); + positions.push_back(pos + u * (r1 * costheta) + v * (r1 * sintheta)); + phi += (s - lastS) * omega; + lastS = s; + } else { + positions.push_back(pos); + positions.push_back(pos); + } + + s += ds; + } +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::drawStroke(const TColorFunction *cf, Points &positions, + const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + tglColor(color); + + glBegin(GL_QUAD_STRIP); + int i = 0; + for (; i < (int)positions.size(); i += 2) { + tglVertex(positions[i]); + tglVertex(positions[i + 1]); + } + glEnd(); + glBegin(GL_LINE_STRIP); + for (i = 0; i < (int)positions.size(); i += 2) { + tglVertex(positions[i]); + } + glEnd(); + glBegin(GL_LINE_STRIP); + for (i = 1; i < (int)positions.size() - 1; i += 2) { + tglVertex(positions[i]); + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +void TFriezeStrokeStyle2::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + using TConsts::pi; + //TStroke *stroke = getStroke(); + double length = stroke->getLength(); + + double s = 0.01; + double lastS = 0; + double phi = 0; + double lastW = 0; + double thick = 1 - m_thick; + vector points; + while (s <= length) { + double w = stroke->getParameterAtLength(s); + if (w < lastW) { + s += 0.1; + continue; + } + lastW = w; + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = normalize(stroke->getSpeed(w)); + TPointD v = rotate90(u); + + double thickness = pos.thick; // 5; //(1-t)*40 + t * 10; + + if (thickness > 0) { + double omega = pi * 2 / (thickness * 2); + + double q = 0.5 * (1 - cos(phi)); + double theta = pi * 0.5 - pi * m_parameter * q; + double r = thickness * sin(phi); + double r1 = r * thick; + double costheta = cos(theta); + double sintheta = sin(theta); + points.push_back(pos + u * (r * costheta) + v * (r * sintheta)); + points.push_back(pos + u * (r1 * costheta) + v * (r1 * sintheta)); + phi += (s - lastS) * omega; + lastS = s; + } else { + points.push_back(pos); + points.push_back(pos); + } + + double ds = 0.5; + s += ds; + } + + // Polyline version + flash.setThickness(0.0); + flash.setFillColor(m_color); + for (int i = 0; i < ((int)points.size() - 2); i += 2) { + vector plv; + plv.push_back(points[i]); + plv.push_back(points[i + 1]); + plv.push_back(points[i + 3]); + plv.push_back(points[i + 2]); + flash.drawPolyline(plv); + } + + /* + vector pp; + for( int i=0; i<((int)points.size()-1); i+=2 ) + pp.push_back(TPointD((points[i]+points[i+1])*0.5)); + +// Quadratic version + + if ( pp.size()<=2 ) { + if ( pp.size()==2 ) { + vector sv; + sv.push_back(TSegment(pp[0],pp[1])); + flash.setLineColor(m_color); + flash.drawSegments(sv,false); + } + return; + } + + vector qv; + qv.push_back(TQuadratic(pp[i],pp[i]*0.75+pp[i+1]*0.25,(pp[i]+pp[i+1])*0.5)); + for( i=1; i<(int)(pp.size()-1); i++ ) { + TPointD p0=((pp[i-1]+pp[i])*0.5); + TPointD p1=pp[i]; + TPointD p2=((pp[i]+pp[i+1])*0.5); + qv.push_back(TQuadratic(p0,p1,p2)); + } + int n=pp.size()-1; + qv.push_back(TQuadratic((pp[n-1]+pp[n])*0.5,pp[n-1]*0.25+pp[n]*0.75,pp[n])); + + flash.setLineColor(m_color); + flash.setThickness(m_thick); + flash.drawquads(qv); + +*/ + // Segment version + /* + flash.setThickness(m_thick); + flash.setLineColor(m_color); + for( i=0; i<(int)(pp.size()-1); i++ ) { + vector sv; + sv.push_back(TSegment(pp[i],pp[i+1])); + flash.drawSegments(sv,false); + } +*/ +} + +//============================================================================= + +TDualColorStrokeStyle2::TDualColorStrokeStyle2( + TPixel32 color0, TPixel32 color1, double parameter) + : m_color0(color0), m_color1(color1), m_parameter(parameter) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TDualColorStrokeStyle2::clone() const +{ + return new TDualColorStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +void TDualColorStrokeStyle2::computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const +{ + param.m_lengthStep = m_parameter; + TOutlineStyle::computeOutline(stroke, outline, param); +} + +//----------------------------------------------------------------------------- + +void TDualColorStrokeStyle2::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, + const TStroke *stroke) const +{ + UINT i; + const std::vector &v = outline->getArray(); + TPixel32 colorv[2]; + + if (cf) { + colorv[0] = (*(cf))(m_color0); + colorv[1] = (*(cf))(m_color1); + } else { + colorv[0] = m_color0; + colorv[1] = m_color1; + } + int colorindex = 0; + if (v.empty()) + return; + glBegin(GL_LINE_STRIP); + tglColor(colorv[0]); + for (i = 0; i < v.size(); i += 2) { + glVertex2dv(&v[i].x); + if (0 != v[i].stepCount) { + colorindex++; + tglColor(colorv[colorindex & 1]); + glVertex2dv(&v[i].x); + } + } + glEnd(); + + colorindex = 0; + glBegin(GL_LINE_STRIP); + tglColor(colorv[0]); + for (i = 1; i < v.size(); i += 2) { + glVertex2dv(&v[i].x); + if (0 != v[i].stepCount) { + colorindex++; + tglColor(colorv[colorindex & 1]); + glVertex2dv(&v[i].x); + } + } + glEnd(); + + colorindex = 0; + glBegin(GL_QUAD_STRIP); + tglColor(colorv[0]); + for (i = 0; i < v.size(); i += 2) { + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + if (0 != v[i].stepCount) { + colorindex++; + tglColor(colorv[colorindex & 1]); + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + } + } + glEnd(); + + //antialias delle linee normali + tglColor(colorv[0]); + for (i = 0; i < v.size(); i += 2) { + if (0 != v[i].stepCount) { + glBegin(GL_LINES); + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + glEnd(); + } + } +} + +//----------------------------------------------------------------------------- + +void TDualColorStrokeStyle2::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + + TOutlineUtil::OutlineParameter param; + param.m_lengthStep = m_parameter; + TStrokeOutline outline; + TOutlineStyle::computeOutline(stroke, outline, param); + const std::vector &v = outline.getArray(); + if (v.empty()) + return; + + TPixel32 colorv[2] = {m_color0, m_color1}; + int colorindex = 0; + flash.setThickness(0.0); + flash.setFillColor(TPixel32(0, 0, 0, 255)); + for (UINT i = 0; i < (v.size() - 2); i += 2) { + vector tpv; + tpv.push_back(TPointD(v[i].x, v[i].y)); + tpv.push_back(TPointD(v[i + 1].x, v[i + 1].y)); + tpv.push_back(TPointD(v[i + 3].x, v[i + 3].y)); + tpv.push_back(TPointD(v[i + 2].x, v[i + 2].y)); + + if (0 != v[i].stepCount) { + colorindex++; + flash.setFillColor(colorv[colorindex & 1]); + } + flash.drawPolyline(tpv); + + /* --- testing --- + vector s; + s.push_back(TSegment(tpv[0],tpv[1])); + s.push_back(TSegment(tpv[1],tpv[2])); + s.push_back(TSegment(tpv[2],tpv[3])); + s.push_back(TSegment(tpv[3],tpv[0])); + flash.drawSegments(s,false); +*/ + } +} + +//----------------------------------------------------------------------------- + +int TDualColorStrokeStyle2::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TDualColorStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TDualColorStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return QCoreApplication::translate("TDualColorStrokeStyle2", "Distance"); +} + +//----------------------------------------------------------------------------- + +void TDualColorStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 1.0; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TDualColorStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_parameter; +} + +//----------------------------------------------------------------------------- + +void TDualColorStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_parameter = value; + updateVersionNumber(); //questo si chiama per ogno cambiamento di parametro per cui di deve ricalcolare l'outline +} + +//============================================================================= + +TLongBlendStrokeStyle2::TLongBlendStrokeStyle2( + TPixel32 color0, TPixel32 color1, double parameter) + : m_color0(color0), m_color1(color1), m_parameter(parameter) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TLongBlendStrokeStyle2::clone() const +{ + return new TLongBlendStrokeStyle2(*this); +} + +//----------------------------------------------------------------------------- + +void TLongBlendStrokeStyle2::computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const +{ + param.m_lengthStep = m_parameter; + TOutlineStyle::computeOutline(stroke, outline, param); +} + +//----------------------------------------------------------------------------- + +void TLongBlendStrokeStyle2::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, + const TStroke *stroke) const +{ + TPixel32 color0, color1; + if (cf) { + color0 = (*(cf))(m_color0); + color1 = (*(cf))(m_color1); + } else { + color0 = m_color0; + color1 = m_color1; + } + UINT i; + + const std::vector &v = outline->getArray(); + if (v.empty()) + return; + + // outline with antialiasing + glBegin(GL_LINE_STRIP); + int mystepCount = 0; + + double totallength = stroke->getLength(); + double ntick = totallength / m_parameter + 1; + tglColor(color0); + for (i = 0; i < v.size(); i += 2) { + if (0 != v[i].stepCount) { + tglColor(blend(color0, color1, (double)mystepCount / ntick)); + mystepCount++; + } + glVertex2dv(&v[i].x); + } + glEnd(); + + glBegin(GL_LINE_STRIP); + mystepCount = 0; + tglColor(color0); + for (i = 1; i < v.size(); i += 2) { + if (0 != v[i].stepCount) { + tglColor(blend(color0, color1, (double)mystepCount / ntick)); + mystepCount++; + } + glVertex2dv(&v[i].x); + } + glEnd(); + + glBegin(GL_QUAD_STRIP); + mystepCount = 0; + tglColor(color0); + for (i = 0; i < v.size(); i += 2) { + if (0 != v[i].stepCount) { + tglColor(blend(color0, color1, (double)mystepCount / ntick)); + mystepCount++; + } + glVertex2dv(&v[i].x); + glVertex2dv(&v[i + 1].x); + } + glEnd(); +} + +//----------------------------------------------------------------------------- + +void TLongBlendStrokeStyle2::drawStroke(TFlash &flash, const TStroke *stroke) const +{ + TPixel32 color0, color1; + color0 = m_color0; + color1 = m_color1; + + UINT i; + TOutlineUtil::OutlineParameter param; + double lParameter = m_parameter >= 20.0 || m_parameter < 0.0 ? 10.0 : m_parameter; + param.m_lengthStep = lParameter; + TStrokeOutline outline; + TOutlineStyle::computeOutline(stroke, outline, param); + const std::vector &v = outline.getArray(); + if (v.empty()) + return; + + flash.setThickness(0.0); + + int mystepCount = 0; + double totallength = stroke->getLength(); + double ntick = totallength / lParameter + 1; + SFlashUtils sfu; + TPixel32 col0, col1; + col0 = col1 = color0; + for (i = 2; i < v.size(); i += 2) { + if (0 != v[i].stepCount) { + col1 = blend(color0, color1, (double)mystepCount / ntick); + mystepCount++; + } + vector plv; + plv.push_back(TPointD(v[i - 2].x, v[i - 2].y)); + plv.push_back(TPointD(v[i - 1].x, v[i - 1].y)); + plv.push_back(TPointD(v[i + 1].x, v[i + 1].y)); + plv.push_back(TPointD(v[i].x, v[i].y)); + sfu.drawGradedPolyline(flash, plv, col0, col1); + col0 = col1; + } +} + +//----------------------------------------------------------------------------- + +int TLongBlendStrokeStyle2::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TLongBlendStrokeStyle2::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TLongBlendStrokeStyle2::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + return "Distance"; //W_Watercolor_Distance +} + +//----------------------------------------------------------------------------- + +void TLongBlendStrokeStyle2::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + min = 0.1; + max = 100.0; +} + +//----------------------------------------------------------------------------- + +double TLongBlendStrokeStyle2::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + return m_parameter; +} + +//----------------------------------------------------------------------------- + +void TLongBlendStrokeStyle2::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + m_parameter = value; + updateVersionNumber(); //questo si chiama per ogno cambiamento di parametro per cui di deve ricalcolare l'outline +} + +//============================================================================= + +#ifdef _DEBUG + +OutlineViewerStyle::OutlineViewerStyle( + TPixel32 color, double parameter0, double parameter1, + double parameter2, double parameter3) + : TSolidColorStyle(color), m_boolPar(false), m_intPar(1), m_enumPar(2), m_pathPar("testPath") +{ + m_parameter[0] = parameter0; + m_parameter[1] = parameter1; + m_parameter[2] = parameter2; + m_parameter[3] = parameter3; +} + +//----------------------------------------------------------------------------- + +TColorStyle *OutlineViewerStyle::clone() const +{ + return new OutlineViewerStyle(*this); +} + +//----------------------------------------------------------------------------- + +int OutlineViewerStyle::getParamCount() const +{ + return 8; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType OutlineViewerStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + static const TColorStyle::ParamType types[8] = + {TColorStyle::DOUBLE, TColorStyle::DOUBLE, TColorStyle::DOUBLE, TColorStyle::DOUBLE, + TColorStyle::BOOL, TColorStyle::INT, TColorStyle::ENUM, TColorStyle::FILEPATH}; + + return types[index]; +} + +//----------------------------------------------------------------------------- + +QString OutlineViewerStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 8); + + switch (index) { + case 0: + return QCoreApplication::translate("OutlineViewerStyle", "Control Point"); + case 1: + return QCoreApplication::translate("OutlineViewerStyle", "Center Line"); + case 2: + return QCoreApplication::translate("OutlineViewerStyle", "Outline Mode"); + case 3: + return QCoreApplication::translate("OutlineViewerStyle", "Distance"); + case 4: + return "Bool"; + case 5: + return "Int"; + case 6: + return "Enum"; + case 7: + return "Path"; + } + + assert(0); + return QCoreApplication::translate("OutlineViewerStyle", "distance"); +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 4); + switch (index) { + case 0: + min = 0.0; + max = 2.99; + + CASE 1 : min = 0.0; + max = 1.99; + + CASE 2 : min = 0.0; + max = 3.99; + + CASE 3 : min = 3.0; + max = 100.0; + } +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::getParamRange(int index, int &min, int &max) const +{ + assert(5 <= index && index < 7); + switch (index) { + case 5: + min = 0, max = 10; + + CASE 6 : min = 0, max = 4; + } +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::getParamRange(int index, QStringList &enumItems) const +{ + enumItems << "Prova 1" + << "Prova 2" + << "Prova 3" + << "Prova 4"; +} + +//----------------------------------------------------------------------------- + +bool OutlineViewerStyle::getParamValue(TColorStyle::bool_tag, int index) const +{ + assert(index == 4); + return m_boolPar; +} + +//----------------------------------------------------------------------------- + +int OutlineViewerStyle::getParamValue(TColorStyle::int_tag, int index) const +{ + assert(5 <= index && index < 7); + return (index == 5) ? m_intPar : m_enumPar; +} + +//----------------------------------------------------------------------------- + +double OutlineViewerStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 4); + return m_parameter[index]; +} + +//----------------------------------------------------------------------------- + +TFilePath OutlineViewerStyle::getParamValue(TColorStyle::TFilePath_tag, int index) const +{ + assert(index == 7); + return m_pathPar; +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::setParamValue(int index, bool value) +{ + assert(index == 4); + m_boolPar = value; +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::setParamValue(int index, int value) +{ + assert(5 <= index && index < 7); + (index == 5) ? m_intPar = value : m_enumPar = value; +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 4); + + if (index >= 2 && (int)m_parameter[index] != (int)value) //cambia l'outline + { + updateVersionNumber(); + } + + m_parameter[index] = value; +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::setParamValue(int index, const TFilePath &value) +{ + assert(index == 7); + m_pathPar = value; +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::loadData(TInputStreamInterface &is) +{ + TPixel32 color; + is >> color; + TSolidColorStyle::setMainColor(color); + is >> m_parameter[0]; + is >> m_parameter[1]; + is >> m_parameter[2]; + is >> m_parameter[3]; + + int boolPar; + is >> boolPar; + m_boolPar = boolPar; + is >> m_intPar; + is >> m_enumPar; + std::string str; + is >> str; + m_pathPar = TFilePath(toWideString(str)); +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::saveData(TOutputStreamInterface &os) const +{ + os << TSolidColorStyle::getMainColor(); + os << m_parameter[0]; + os << m_parameter[1]; + os << m_parameter[2]; + os << m_parameter[3]; + + os << int(m_boolPar); + os << m_intPar; + os << m_enumPar; + os << toString(m_pathPar.getWideString()); +} + +//----------------------------------------------------------------------------- + +void OutlineViewerStyle::computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const +{ + if (m_parameter[2] >= 1.0) { + param.m_lengthStep = (m_parameter[2] >= 3.0) ? m_parameter[3] : 0; + + TOutlineStyle::computeOutline(stroke, outline, param); + } +} + +//----------------------------------------------------------------------------- +namespace +{ + +void drawOutline(TStrokeOutline *outline, bool cut) +{ + const std::vector &v = outline->getArray(); + + if (v.empty()) + return; + + UINT i; + // outline with antialiasing + glBegin(GL_LINE_STRIP); + for (i = 0; i < v.size(); i += 2) + glVertex2dv(&v[i].x); + glEnd(); + + glBegin(GL_LINE_STRIP); + for (i = 1; i < v.size(); i += 2) + glVertex2dv(&v[i].x); + glEnd(); + + if (cut) { + static const int stride = sizeof(TOutlinePoint); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_DOUBLE, stride, &v[0]); + glDrawArrays(GL_LINES, 0, v.size()); + glDisableClientState(GL_VERTEX_ARRAY); + } +} + +void drawControlPoints(const TStroke *stroke, bool allPoints) +{ + int i; + TPointD p; + glPointSize(4.0); + glBegin(GL_POINTS); + + if (allPoints) { + int n = stroke->getControlPointCount(); + for (i = 0; i < n; ++i) { + p = stroke->getControlPoint(i); + glColor3d((i + 1) & 1, i & 1, 0.0); + glVertex2d(p.x, p.y); + } + } else { + int n = stroke->getChunkCount(); + for (i = 0; i < n; ++i) { + const TThickQuadratic *chunk = stroke->getChunk(i); + p = chunk->getP0(); + glColor3d(1.0, 0.0, 0.0); + glVertex2d(p.x, p.y); + } + const TThickQuadratic *chunk = stroke->getChunk(n - 1); + glColor3d(1.0, 0.0, 0.0); + p = chunk->getP2(); + glVertex2d(p.x, p.y); + } + + glEnd(); +} + +void drawCenterline(const TStroke *stroke) +{ + glBegin(GL_LINE_STRIP); + int n = stroke->getChunkCount(); + int i = 0; + for (i = 0; i < n; ++i) { + const TThickQuadratic *chunk = stroke->getChunk(i); + double length = chunk->getLength(0, 1); + int maxCount = tmax(tceil(length / (5 * sqrt(tglGetPixelSize2()))), 1); + double deltaT = 1.0 / maxCount; + double t = 0; + for (t = 0; t < 1 + (deltaT / 2); t += deltaT) { + TPointD point = chunk->getPoint(t); + glVertex2d(point.x, point.y); + } + } + glEnd(); + return; +} +} +//------------------------------------------------------------ + +void OutlineViewerStyle::drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const +{ + TPixel32 color; + if (cf) + color = (*(cf))(getMainColor()); + else + color = getMainColor(); + + tglColor(color); + + if (m_parameter[1] >= 1.0) + drawCenterline(stroke); + + if (m_parameter[2] >= 1.0) + drawOutline(outline, m_parameter[2] >= 2.0); + + if (m_parameter[0] >= 1.0) + drawControlPoints(stroke, m_parameter[0] >= 2.0); +} + +#endif + +//============================================================================= + +TMatrioskaStrokeProp::TMatrioskaStrokeProp(const TStroke *stroke, TMatrioskaStrokeStyle *style) + : TStrokeProp(stroke), m_colorStyle(style), m_outline(), m_outlinePixelSize(0) +{ + m_styleVersionNumber = m_colorStyle->getVersionNumber(); +} + +//----------------------------------------------------------------------------- + +TStrokeProp *TMatrioskaStrokeProp::clone(const TStroke *stroke) const +{ + TMatrioskaStrokeProp *prop = new TMatrioskaStrokeProp(stroke, m_colorStyle); + prop->m_strokeChanged = m_strokeChanged; + prop->m_outline = m_outline; + prop->m_outlinePixelSize = m_outlinePixelSize; + return prop; +} + +//----------------------------------------------------------------------------- + +const TColorStyle *TMatrioskaStrokeProp::getColorStyle() const +{ + return m_colorStyle; +} + +//----------------------------------------------------------------------------- + +namespace +{ +void recomputeStrokes(const TStroke *stroke, vector &strokes, int strokeNumber) +{ + clearPointerContainer(strokes); + + strokes.resize(strokeNumber); + + int nCP = stroke->getControlPointCount(); + + double reduction; + + for (int strokeId = 0; strokeId != strokeNumber; strokeId++) { + strokes[strokeId] = new TStroke(*stroke); + + reduction = ((double)strokeId + 0.5) / (double)(strokeNumber + 0.5); + + for (int i = 0; i < nCP; i++) { + TThickPoint tp = strokes[strokeId]->getControlPoint(i); + tp.thick *= reduction; + strokes[strokeId]->setControlPoint(i, tp); + } + } +} + +void recomputeOutlines(const TStroke *stroke, vector &strokes, vector &outlines, const TSolidColorStyle *style) +{ + TOutlineUtil::OutlineParameter param; + int strokeNumber = strokes.size(); + outlines.resize(strokeNumber + 1); + int strokeId; + for (strokeId = 0; strokeId != strokeNumber; strokeId++) { + outlines[strokeId].getArray().clear(); + style->computeOutline(strokes[strokeId], outlines[strokeId], param); + } + outlines[strokeId].getArray().clear(); + style->computeOutline(stroke, outlines[strokeId], param); +} +}; + +void TMatrioskaStrokeProp::draw(const TVectorRenderData &rd) +{ + if (rd.m_clippingRect != TRect() && !rd.m_is3dView && !convert(rd.m_aff * m_stroke->getBBox()).overlaps(rd.m_clippingRect)) + return; + + int strokeId; + glPushMatrix(); + tglMultMatrix(rd.m_aff); + + double pixelSize = sqrt(tglGetPixelSize2()); + int strokeNumber = (int)(m_colorStyle->getParamValue(TColorStyle::double_tag(), 0)) - 1; + + if (m_strokeChanged || (UINT)strokeNumber != m_appStrokes.size()) { + m_strokeChanged = false; + m_outlinePixelSize = pixelSize; + recomputeStrokes(m_stroke, m_appStrokes, strokeNumber); + recomputeOutlines(m_stroke, m_appStrokes, m_outline, m_colorStyle); + } else if (!isAlmostZero(pixelSize - m_outlinePixelSize, 1e-5) + // || m_styleVersionNumber != m_colorStyle->getVersionNumber() + ) { + m_strokeChanged = false; + m_outlinePixelSize = pixelSize; + recomputeOutlines(m_stroke, m_appStrokes, m_outline, m_colorStyle); + } + + m_colorStyle->drawStroke(rd.m_cf, &m_outline[m_appStrokes.size()], m_stroke); + + TSolidColorStyle appStyle(m_colorStyle->getColorParamValue(1)); + + // if(m_colorStyle->isAlternate()) + // { + + for (strokeId = strokeNumber - 1; strokeId >= 0; strokeId--) { + if ((m_appStrokes.size() - strokeId) & 1) + appStyle.drawStroke(rd.m_cf, &m_outline[strokeId], m_appStrokes[strokeId]); + else + m_colorStyle->drawStroke(rd.m_cf, &m_outline[strokeId], m_appStrokes[strokeId]); + } + + // } + // else + // { + // TPixel32 color0=m_colorStyle->getColorParamValue(0); + // TPixel32 color1=m_colorStyle->getColorParamValue(1); + + // for(strokeId=strokeNumber-1; strokeId>=0;strokeId--) + // { + // double r2=(double)strokeId/(double)(strokeNumber); + // double r1=1-r2; + // TPixel32 color((int)(color0.r*r2+color1.r*r1), + // (int)(color0.g*r2+color1.g*r1), + // (int)(color0.b*r2+color1.b*r1), + // (int)(color0.m*r2+color1.m*r1) + // ); + + // appStyle.setMainColor(color); + // appStyle.drawStroke(rd.m_cf, &m_outline[strokeId],m_appStrokes[strokeId]); + // } + + // } + + glPopMatrix(); +} + +//------------------------------------------------------------------------------------------ + +void TMatrioskaStrokeProp::draw(TFlash &flash) +{ + int strokeId, strokeNumber = (int)(m_colorStyle->getParamValue(TColorStyle::double_tag(), 0)) - 1; + if ((UINT)strokeNumber != m_appStrokes.size()) { + recomputeStrokes(m_stroke, m_appStrokes, strokeNumber); + } + + m_colorStyle->TOutlineStyle::drawStroke(flash, m_stroke); + + TSolidColorStyle appStyle(m_colorStyle->getColorParamValue(1)); + + for (strokeId = strokeNumber - 1; strokeId >= 0; strokeId--) { + if ((m_appStrokes.size() - strokeId) & 1) + appStyle.TOutlineStyle::drawStroke(flash, m_appStrokes[strokeId]); + else + m_colorStyle->TOutlineStyle::drawStroke(flash, m_appStrokes[strokeId]); + } +} + +//----------------------------------------------------------------------------- + +TMatrioskaStrokeProp::~TMatrioskaStrokeProp() +{ + clearPointerContainer(m_appStrokes); +} + +//============================================================================= + +TMatrioskaStrokeStyle::TMatrioskaStrokeStyle( + TPixel32 color1, TPixel32 color2, double parameter, bool alternate) + : TSolidColorStyle(color1), m_color2(color2), m_parameter(parameter) +{ +} + +//----------------------------------------------------------------------------- + +TColorStyle *TMatrioskaStrokeStyle::clone() const +{ + return new TMatrioskaStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +TStrokeProp *TMatrioskaStrokeStyle::makeStrokeProp(const TStroke *stroke) +{ + return new TMatrioskaStrokeProp(stroke, this); +} + +//----------------------------------------------------------------------------- + +int TMatrioskaStrokeStyle::getParamCount() const +{ + return 1; +} + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType TMatrioskaStrokeStyle::getParamType(int index) const +{ + assert(0 <= index && index < getParamCount()); + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString TMatrioskaStrokeStyle::getParamNames(int index) const +{ + assert(0 <= index && index < 1); + + // if(index) + // return "alter/gradual"; + // else + + return QCoreApplication::translate("TMatrioskaStrokeStyle", "Stripes"); +} + +//----------------------------------------------------------------------------- + +void TMatrioskaStrokeStyle::getParamRange(int index, double &min, double &max) const +{ + assert(0 <= index && index < 1); + + // if(index) + // { + // min = 0; + // max = 1; + // } + // else + // { + + min = 1.0; + max = 10.0; + //} +} + +//----------------------------------------------------------------------------- + +double TMatrioskaStrokeStyle::getParamValue(TColorStyle::double_tag, int index) const +{ + assert(0 <= index && index < 1); + + // if(index) + // return (m_alternate)? 0 : 1; + // else + + return m_parameter; +} + +//----------------------------------------------------------------------------- + +void TMatrioskaStrokeStyle::setParamValue(int index, double value) +{ + assert(0 <= index && index < 1); + + // if(index) + // m_alternate = value<0.5; + // else + + m_parameter = value; + + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +TPixel32 TMatrioskaStrokeStyle::getColorParamValue(int index) const +{ + assert(0 <= index && index < 2); + TPixel32 tmp; + switch (index) { + case (0): + tmp = TSolidColorStyle::getMainColor(); + break; + case (1): + tmp = m_color2; + break; + } + return tmp; +} + +//----------------------------------------------------------------------------- + +void TMatrioskaStrokeStyle::setColorParamValue(int index, const TPixel32 &color) +{ + assert(0 <= index && index < 2); + switch (index) { + case (0): + TSolidColorStyle::setMainColor(color); + break; + case (1): + m_color2 = color; + break; + } +} + +//----------------------------------------------------------------------------- +void TMatrioskaStrokeStyle::loadData(TInputStreamInterface &is) +{ + TSolidColorStyle::loadData(is); + is >> m_parameter; + is >> m_color2; +} + +//------------------------------------------------------------ + +void TMatrioskaStrokeStyle::saveData(TOutputStreamInterface &os) const +{ + TSolidColorStyle::saveData(os); + os << m_parameter; + os << m_color2; +} diff --git a/toonz/sources/colorfx/strokestyles.h b/toonz/sources/colorfx/strokestyles.h new file mode 100644 index 0000000..b79dba1 --- /dev/null +++ b/toonz/sources/colorfx/strokestyles.h @@ -0,0 +1,1179 @@ + + +#ifndef STROKESTYLES_H +#define STROKESTYLES_H + +// TnzCore includes +#include "tsimplecolorstyles.h" +#include "tvectorimage.h" +#include "tstrokeprop.h" +#include "tgl.h" + +class TVectorRendeData; +class TRandom; + +#undef DVAPI +#undef DVVAR + +#ifdef COLORFX_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +//============================================================================= + +typedef struct { + TPointD point; + double dbl1; + double dbl2; +} PointAnd2Double; + +typedef vector Points; + +typedef struct { + float blend; + Points points; +} BlendAndPoint; + +typedef vector> PointsAndColors; +typedef vector PointMatrix; +typedef vector> PointsAndDoubles; +typedef vector> DrawmodePointsMatrix; +typedef vector RectVector; +typedef vector PointsAnd2Doubles; +typedef vector Doubles; +typedef vector BlendAndPoints; +//============================================================================= + +template +class TOptimizedStrokeStyleT : public TColorStyle +{ +public: + TOptimizedStrokeStyleT() {} + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + TStrokeProp *makeStrokeProp(const TStroke *stroke); + + TRegionProp *makeRegionProp(const TRegion *region) + { + assert(false); + return 0; + }; + + virtual void computeData(T &data, const TStroke *stroke, const TColorFunction *cf) const = 0; + virtual void drawStroke(const TColorFunction *cf, T &data, const TStroke *stroke) const = 0; +}; + +//------------------------------------------------------------------- + +class TFurStrokeStyle : public TOptimizedStrokeStyleT +{ + double m_cs, m_sn, m_angle, m_length; + TPixel32 m_color; + +public: + TFurStrokeStyle(); + + TColorStyle *clone() const; + + void invalidate() {} + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + void drawStroke(TFlash &rd, const TStroke *stroke) const; + + QString getDescription() const { return QCoreApplication::translate("TFurStrokeStyle", "Herringbone"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void saveData(TOutputStreamInterface &os) const + { + os << m_color << m_angle << m_length; + } + + void loadData(TInputStreamInterface &is) + { + is >> m_color >> m_angle >> m_length; + m_cs = cos(m_angle); + m_sn = sin(m_angle); + } + + int getTagId() const { return 103; }; +}; + +//------------------------------------------------------------------- + +class TChainStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + +public: + TChainStrokeStyle(const TPixel32 &color); + TChainStrokeStyle(); + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TChainStrokeStyle", "Chain"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void loadData(TInputStreamInterface &is) { is >> m_color; } + void saveData(TOutputStreamInterface &os) const { os << m_color; } + int getTagId() const { return 104; }; +}; + +//------------------------------------------------------------------- + +class TSprayStrokeStyle : public TSimpleStrokeStyle +{ + TPixel32 m_color; + double m_blend, m_intensity, m_radius; + +public: + TSprayStrokeStyle(); + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TSprayStrokeStyle", "Circlets"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_blend >> m_intensity >> m_radius; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_blend << m_intensity << m_radius; } + int getTagId() const { return 106; }; +}; + +//------------------------------------------------------------------- + +class TGraphicPenStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_intensity; + +public: + TGraphicPenStrokeStyle(); + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TGraphicPenStrokeStyle", "Dashes"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void computeData(DrawmodePointsMatrix &data, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, DrawmodePointsMatrix &data, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *s) const; + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_intensity; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_intensity; } + int getTagId() const { return 107; }; +}; + +//------------------------------------------------------------------- + +class TDottedLineStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_in, m_line, m_out, m_blank; + +public: + TDottedLineStrokeStyle(); + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TDottedLineStrokeStyle", "Vanishing"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_in >> m_line >> m_out >> m_blank; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_in << m_line << m_out << m_blank; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 111; } +}; + +//------------------------------------------------------------------- + +class TRopeStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_bend; + +public: + TRopeStrokeStyle(); + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TRopeStrokeStyle", "Rope"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_bend; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_bend; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 108; } +}; + +//------------------------------------------------------------------- + +class TCrystallizeStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_period, m_opacity; + +public: + TCrystallizeStrokeStyle(); + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + TColorStyle *clone() const; + + QString getDescription() const { return QCoreApplication::translate("TCrystallizeStrokeStyle", "Tulle"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_period >> m_opacity; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_period << m_opacity; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 109; } +}; + +//------------------------------------------------------------------- + +class TBraidStrokeStyle : public TSimpleStrokeStyle +{ + TPixel32 m_colors[3]; + double m_period; + +public: + TBraidStrokeStyle(); + + TColorStyle *clone() const; + + void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TBraidStrokeStyle", "Plait"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_colors[0]; } + void setMainColor(const TPixel32 &color) { m_colors[0] = color; } + + int getColorParamCount() const { return 3; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_colors[0] >> m_colors[1] >> m_colors[2] >> m_period; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_colors[0] << m_colors[1] << m_colors[2] << m_period; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 136; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(112); } +}; + +//------------------------------------------------------------------- + +class TSketchStrokeStyle : public TSimpleStrokeStyle +{ + TPixel32 m_color; + double m_density; + +public: + TSketchStrokeStyle(); + + TColorStyle *clone() const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TSketchStrokeStyle", "Fuzz"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &fl, const TStroke *s) const; + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_density; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_density; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 113; } +}; + +//------------------------------------------------------------------- + +class TBubbleStrokeStyle : public TSimpleStrokeStyle +{ + TPixel32 m_color0, m_color1; + +public: + TBubbleStrokeStyle(); + + TColorStyle *clone() const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TBubbleStrokeStyle", "Bubbles"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color0; } + void setMainColor(const TPixel32 &color) { m_color0 = color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const { return index == 0 ? m_color0 : m_color1; } + void setColorParamValue(int index, const TPixel32 &color) + { + if (index == 0) + m_color0 = color; + else + m_color1 = color; + } + + void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void loadData(TInputStreamInterface &is) { is >> m_color0 >> m_color1; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color0 << m_color1; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 114; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(137); } +}; + +//------------------------------------------------------------------- + +class TTissueStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_density, m_border; + +public: + TTissueStrokeStyle(); + + TColorStyle *clone() const; + + void computeData(PointMatrix &data, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, PointMatrix &data, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TTissueStrokeStyle", "Gauze"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_density >> m_border; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_density << m_border; } + + int getTagId() const { return 117; } +}; + +//------------------------------------------------------------------- + +class TBiColorStrokeStyle : public TOutlineStyle +{ + TPixel32 m_color0, m_color1; + double m_parameter; + +public: + TBiColorStrokeStyle(); + + TColorStyle *clone() const; + + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TBiColorStrokeStyle", "Shade"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color0; } + void setMainColor(const TPixel32 &color) { m_color0 = color; } + + void loadData(TInputStreamInterface &is); + void loadData(int oldId, TInputStreamInterface &); + + void saveData(TOutputStreamInterface &os) const; + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const { return index == 0 ? m_color0 : m_color1; } + void setColorParamValue(int index, const TPixel32 &color) + { + if (index == 0) + m_color0 = color; + else + m_color1 = color; + } + + int getTagId() const { return 135; }; + void getObsoleteTagIds(vector &ids) const + { + ids.push_back(115); + ids.push_back(119); + } +}; + +//------------------------------------------------------------------- + +class TNormal2StrokeStyle : public TOutlineStyle +{ + TPixel32 m_color; + double m_lightx, m_lighty, m_shininess, m_metal, m_bend; + +public: + TNormal2StrokeStyle(); + + TColorStyle *clone() const; + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const; + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TNormal2StrokeStyle", "Bump"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_lightx >> m_lighty >> m_shininess >> m_metal >> m_bend; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color << m_lightx << m_lighty + << m_shininess << m_metal << m_bend; } + + int getTagId() const { return 120; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(121); } +}; + +//------------------------------------------------------------------- + +class TChalkStrokeStyle2 : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_blend, m_intensity, m_in, m_out, m_noise; + +public: + TChalkStrokeStyle2(); + + TColorStyle *clone() const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TChalkStrokeStyle2", "Chalk"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void computeData(Doubles &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Doubles &positions, const TStroke *stroke) const; + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_blend >> m_intensity >> m_in >> m_out >> m_noise; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color << m_blend << m_intensity << m_in + << m_out << m_noise; } + int getTagId() const { return 123; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(105); } +}; + +//------------------------------------------------------------------- + +class TBlendStrokeStyle2 : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_blend, m_in, m_out; + +public: + TBlendStrokeStyle2(); + + TColorStyle *clone() const; + + void computeData(PointsAndDoubles &data, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, PointsAndDoubles &data, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TBlendStrokeStyle2", "Fade"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_blend >> m_in >> m_out; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color << m_blend << m_in << m_out; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 125; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(110); } +}; + +//------------------------------------------------------------------- + +class TTwirlStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_period, m_blend; + +public: + TTwirlStrokeStyle(); + + TColorStyle *clone() const; + + void computeData(Doubles &data, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Doubles &data, const TStroke *stroke) const; + //void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TTwirlStrokeStyle", "Ribbon"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_period >> m_blend; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_period << m_blend; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 126; } +}; + +//------------------------------------------------------------------- + +class TSawToothStrokeStyle : public TOutlineStyle +{ + TPixel32 m_color; + double m_parameter; + +public: + TSawToothStrokeStyle(TPixel32 color = TPixel32::Blue, + double parameter = 20.0); + + TColorStyle *clone() const; + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const {} + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void invalidate() {} + + void computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const; + + QString getDescription() const { return QCoreApplication::translate("TSawToothStrokeStyle", "Jagged"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 127; }; + void loadData(TInputStreamInterface &is) { is >> m_color >> m_parameter; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_parameter; } +}; + +//------------------------------------------------------------------- + +class TMultiLineStrokeStyle2 : public TOptimizedStrokeStyleT +{ + TPixel32 m_color0, m_color1; + double m_intensity, m_length, m_thick, m_noise; + +public: + TMultiLineStrokeStyle2(); + + TColorStyle *clone() const; + + void computeData(BlendAndPoints &data, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, BlendAndPoints &data, const TStroke *stroke) const; + //void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TMultiLineStrokeStyle2", "Gouache"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color0; } + void setMainColor(const TPixel32 &color) { m_color0 = color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const { return index == 0 ? m_color0 : m_color1; } + void setColorParamValue(int index, const TPixel32 &color) + { + if (index == 0) + m_color0 = color; + else + m_color1 = color; + } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color0 >> m_color1 >> m_intensity >> m_length >> m_thick >> m_noise; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color0 << m_color1 << m_intensity << m_length << m_thick << m_noise; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 138; }; + void getObsoleteTagIds(vector &ids) const + { + ids.push_back(118); + ids.push_back(128); + } +}; + +//------------------------------------------------------------------- + +class TZigzagStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_minDist, m_maxDist; + double m_minAngle, m_maxAngle; + double m_thickness; + + //void drawBLines(RectVector& rects) const; + void setRealMinMax() const; + bool getZigZagPosition(const TStroke *stroke, TRandom &rnd, + const double s, const int first, + const double minTranslLength, + TThickPoint &pos, TThickPoint &pos1) const; + +public: + TZigzagStrokeStyle(); + + TColorStyle *clone() const; + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + //void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TZigzagStrokeStyle", "Zigzag"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_minDist >> m_maxDist >> m_minAngle >> m_maxAngle >> m_thickness; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_minDist << m_maxDist << m_minAngle << m_maxAngle << m_thickness; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 129; } +}; + +//------------------------------------------------------------------- + +class TSinStrokeStyle : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_frequency, m_thick; + +public: + TSinStrokeStyle(); + + TColorStyle *clone() const; + + void computeData(Points &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, Points &positions, const TStroke *stroke) const; + //void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TSinStrokeStyle", "Wave"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_frequency >> m_thick; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_frequency << m_thick; } + + int getTagId() const { return 130; } +}; + +//------------------------------------------------------------------- + +class TFriezeStrokeStyle2 : public TOptimizedStrokeStyleT +{ + TPixel32 m_color; + double m_parameter, m_thick; + +public: + TFriezeStrokeStyle2(); + + TColorStyle *clone() const; + + void invalidate() {} + + QString getDescription() const { return QCoreApplication::translate("TFriezeStrokeStyle2", "Curl"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color; } + void setMainColor(const TPixel32 &color) { m_color = color; } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + void computeData(vector &positions, const TStroke *stroke, const TColorFunction *cf) const; + void drawStroke(const TColorFunction *cf, vector &positions, const TStroke *stroke) const; + //void drawStroke(const TColorFunction *cf, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_parameter >> m_thick; } + void loadData(int oldId, TInputStreamInterface &); + void saveData(TOutputStreamInterface &os) const { os << m_color << m_parameter << m_thick; } + int getTagId() const { return 133; }; + void getObsoleteTagIds(vector &ids) const { ids.push_back(102); } +}; + +//------------------------------------------------------------------- + +class TDualColorStrokeStyle2 : public TOutlineStyle +{ + TPixel32 m_color0, m_color1; + double m_parameter; + +public: + TDualColorStrokeStyle2( + TPixel32 color0 = TPixel32::Blue, + TPixel32 color1 = TPixel32::Yellow, + double parameter = 20.0); + + TColorStyle *clone() const; + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const {} + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void invalidate() {} + + void computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const; + + QString getDescription() const { return QCoreApplication::translate("TDualColorStrokeStyle2", "Striped"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color0; } + void setMainColor(const TPixel32 &color) { m_color0 = color; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const { return (index == 0) ? m_color0 : m_color1; } + void setColorParamValue(int index, const TPixel32 &color) + { + if (index == 0) + m_color0 = color; + else + m_color1 = color; + } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 132; }; + void loadData(TInputStreamInterface &is) { is >> m_color0 >> m_color1 >> m_parameter; } + void saveData(TOutputStreamInterface &os) const { os << m_color0 << m_color1 << m_parameter; } +}; + +//------------------------------------------------------------------- + +class TLongBlendStrokeStyle2 : public TOutlineStyle +{ + TPixel32 m_color0, m_color1; + double m_parameter; + +public: + TLongBlendStrokeStyle2(TPixel32 color0 = TPixel32::Blue, + TPixel32 color1 = TPixel32::Transparent, + double parameter = 20.0); + + TColorStyle *clone() const; + + void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const {} + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + void drawStroke(TFlash &flash, const TStroke *stroke) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void invalidate() {} + + void computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const; + + QString getDescription() const { return QCoreApplication::translate("TLongBlendStrokeStyle2", "Watercolor"); } + + bool hasMainColor() const { return true; } + TPixel32 getMainColor() const { return m_color0; } + void setMainColor(const TPixel32 &color0) { m_color0 = color0; } + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const { return index == 0 ? m_color0 : m_color1; } + void setColorParamValue(int index, const TPixel32 &color) + { + if (index == 0) + m_color0 = color; + else + m_color1 = color; + } + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + int getTagId() const { return 134; }; + void loadData(TInputStreamInterface &is) { is >> m_color0 >> m_color1 >> m_parameter; } + void saveData(TOutputStreamInterface &os) const { os << m_color0 << m_color1 << m_parameter; } +}; + +//------------------------------------------------------------------- + +#ifdef _DEBUG + +class OutlineViewerStyle : public TSolidColorStyle +{ + double m_parameter[4]; + + bool m_boolPar; + int m_intPar; + int m_enumPar; + TFilePath m_pathPar; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +public: + OutlineViewerStyle(TPixel32 color = TPixel32::Black, + double parameter0 = 0.0, double parameter1 = 0.0, + double parameter2 = 2.0, double parameter3 = 3.0); + + TColorStyle *clone() const; + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + + bool getParamValue(TColorStyle::bool_tag, int index) const; + void setParamValue(int index, bool value); + + void getParamRange(int index, int &min, int &max) const; + int getParamValue(TColorStyle::int_tag, int index) const; + void setParamValue(int index, int value); + + void getParamRange(int index, QStringList &enumItems) const; + + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + TFilePath getParamValue(TColorStyle::TFilePath_tag, int index) const; + void setParamValue(int index, const TFilePath &path); + + void computeOutline(const TStroke *stroke, + TStrokeOutline &outline, + TOutlineUtil::OutlineParameter param) const; + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + void drawStroke(const TColorFunction *cf, TStrokeOutline *outline, const TStroke *stroke) const; + QString getDescription() const { return QCoreApplication::translate("OutlineViewerStyle", "OutlineViewer(OnlyDebug)"); } + int getTagId() const { return 99; }; +}; + +#endif + +//------------------------------------------------------------------- + +class TMatrioskaStrokeStyle; + +class TMatrioskaStrokeProp : public TStrokeProp +{ + +protected: + double m_outlinePixelSize; + TMatrioskaStrokeStyle *m_colorStyle; + + vector m_outline; + + vector m_appStrokes; + +public: + TMatrioskaStrokeProp(const TStroke *stroke, TMatrioskaStrokeStyle *style); + ~TMatrioskaStrokeProp(); + + const TColorStyle *getColorStyle() const; + + TStrokeProp *clone(const TStroke *stroke) const; + void draw(const TVectorRenderData &rd); + void draw(TFlash &flash); +}; + +class TMatrioskaStrokeStyle : public TSolidColorStyle +{ + double m_parameter; + TPixel32 m_color2; + +protected: + void loadData(TInputStreamInterface &is); + void saveData(TOutputStreamInterface &os) const; + +public: + TMatrioskaStrokeStyle( + TPixel32 color1 = TPixel32::Magenta, + TPixel32 color2 = TPixel32::Blue, + double parameter = 6.0, + bool alternate = true); + + TColorStyle *clone() const; + + int getColorParamCount() const { return 2; } + TPixel32 getColorParamValue(int index) const; + void setColorParamValue(int index, const TPixel32 &color); + + int getParamCount() const; + TColorStyle::ParamType getParamType(int index) const; + + QString getParamNames(int index) const; + void getParamRange(int index, double &min, double &max) const; + double getParamValue(TColorStyle::double_tag, int index) const; + void setParamValue(int index, double value); + + TStrokeProp *makeStrokeProp(const TStroke *stroke); + + bool isRegionStyle() const { return false; } + bool isStrokeStyle() const { return true; } + + QString getDescription() const { return QCoreApplication::translate("TMatrioskaStrokeStyle", "Toothpaste"); } + int getTagId() const { return 141; }; +}; + +#endif // STROKESTYLES_H diff --git a/toonz/sources/colorfx/zigzagstyles.cpp b/toonz/sources/colorfx/zigzagstyles.cpp new file mode 100644 index 0000000..d870e78 --- /dev/null +++ b/toonz/sources/colorfx/zigzagstyles.cpp @@ -0,0 +1,183 @@ + + +/*test*/ +#include "zigzagstyles.h" +//#include "tstrokeoutline.h" +#include "trandom.h" +//#include "tgl.h" + +#ifdef POICIPENSO + +TZigzagStrokeStyle::TZigzagStrokeStyle(const TPixel32 &color) + : m_color(color), m_density(0.5) +{ +} + +//----------------------------------------------------------------------------- + +TZigzagStrokeStyle::TZigzagStrokeStyle() + : m_color(TPixel32(0, 0, 0, 255)), m_density(0.5) +{ +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::draw(double pixelSize, const TColorFunction *) +{ + // Distance between sampling points + const double minDist = 0.5; + const double maxDist = 6.0; + // Rotation angle of tangent + const int minRotAngle = 0; + const int maxRotAngle = 10; + // e.g minimum translation length is the half of the thickness + const double minTranslLength = 0.7; + + TStroke *stroke = getStroke(); + if (!stroke) + return; + double length = stroke->getLength(); + if (length <= 0) + return; + + int count = (int)(length / (minDist + (maxDist - minDist) * (1.0 - m_density)) + 1); + double dist = length / count; + + TRandom rnd; + // glEnable(GL_BLEND); + tglColor(m_color); + glBegin(GL_LINE_STRIP); + int first = 1; + for (double s = 0; s < (length + dist); s += dist, first = -first) { + double w = stroke->getParameterAtLength(s); + TThickPoint pos = stroke->getThickPoint(w); + TPointD u = stroke->getSpeed(w); + if (norm2(u) < TConsts::epsilon) + continue; + u = normalize(u); + int angle = rnd.getInt(minRotAngle, maxRotAngle); + TRotation rotM(angle); + u = rotM * u; + double maxTranslLength = angle == 90 ? 1.0 : 2.0; + if (angle > 30 && angle < 90) { + double rta = 1.0 / tan(degree2rad(angle)); + maxTranslLength = sqrt(sq(rta) + 1.0); + } + double r = (minTranslLength + (maxTranslLength - minTranslLength) * rnd.getDouble()) * pos.thick * first; + glVertex(pos + r * u); + } + glEnd(); + // glDisable(GL_BLEND); + // glColor4d(0,0,0,1); +} + +//----------------------------------------------------------------------------- + +void TZigzagStrokeStyle::changeParameter(double delta) +{ + m_density = tcrop(m_density + delta, 0.0, 1.0); +} + +//----------------------------------------------------------------------------- + +TStrokeStyle *TZigzagStrokeStyle::clone() const +{ + TColorStyle *cs = new TZigzagStrokeStyle(*this); + cs->assignNames(this); + return cs; +} + +//============================================================================= + +TImageBasedZigzagStrokeStyle::TImageBasedZigzagStrokeStyle(const TPixel32 &color) + : m_color(color), m_textScale(1.0), m_texture(0) + +{ +} + +//----------------------------------------------------------------------------- + +TImageBasedZigzagStrokeStyle::TImageBasedZigzagStrokeStyle() + : m_color(TPixel32(0, 0, 0, 255)), m_textScale(1.0), m_texture(0) +{ +} + +//----------------------------------------------------------------------------- + +TImageBasedZigzagStrokeStyle::TImageBasedZigzagStrokeStyle(const TRaster32P texture) + : m_color(TPixel32(0, 0, 0, 255)), m_textScale(1.0), m_texture(texture) +{ +} + +//----------------------------------------------------------------------------- + +inline int TImageBasedZigzagStrokeStyle::realTextCoord(const int a, const int l) const +{ + return a == 0 ? 0 : (a > 0 ? a % l : l - 1 - ((-a) % l)); +} + +//----------------------------------------------------------------------------- + +void TImageBasedZigzagStrokeStyle::draw(double pixelSize, const TColorFunction *) +{ + // Distance between sampling points + const double dist = 1.0; + // Scaling of texture + double recScale = fabs(m_textScale) < TConsts::epsilon ? 1.0 : 1.0 / m_textScale; + + TStroke *stroke = getStroke(); + if (!stroke) + return; + double length = stroke->getLength(); + if (length <= 0) + return; + + TRandom rnd; + // glEnable(GL_BLEND); + glColor(m_color); + glBegin(GL_LINE_STRIP); + TPointD textC = m_texture->getCenterD(); + TRectD strokeBB = stroke->getBBox(); + TPointD strokeC((strokeBB.x0 + strokeBB.x1) / 2, (strokeBB.y0 + strokeBB.y1) / 2); + for (double s = 0; s < (length + dist); s += dist) { + double w = stroke->getParameterAtLength(s); + TThickPoint pos = stroke->getThickPoint(w); + TPointD textPos((pos.x - strokeC.x) * recScale + textC.x, (pos.y - strokeC.y) * recScale + textC.y); + TPointD u = stroke->getSpeed(w); + if (norm2(u) == 0) + continue; + u = normalize(u); + + TPoint iTextPos(tround(textPos.x), tround(textPos.y)); + iTextPos.x = realTextCoord(iTextPos.x, m_texture->getLx()); + iTextPos.y = realTextCoord(iTextPos.y, m_texture->getLy()); + TPixel32 color = m_texture->pixels(iTextPos.y)[iTextPos.x]; + double ch, cl, cs; + RGB2HLS(color.r / 255.0, color.g / 255.0, color.b / 255.0, &ch, &cl, &cs); + TRotation rotM(tround(ch)); + u = rotM * u; + double r = cl * pos.thick; + glVertex(pos + r * u); + } + glEnd(); + // glDisable(GL_BLEND); + // glColor4d(0,0,0,1); +} + +//----------------------------------------------------------------------------- + +void TImageBasedZigzagStrokeStyle::changeParameter(double delta) +{ + m_textScale = tcrop(m_textScale + delta, 0.1, 2.5); +} + +//----------------------------------------------------------------------------- + +TStrokeStyle *TImageBasedZigzagStrokeStyle::clone() const +{ + TColorStyle *cs = new TImageBasedZigzagStrokeStyle(*this); + cs->assignNames(this); + return cs; +} + +#endif diff --git a/toonz/sources/colorfx/zigzagstyles.h b/toonz/sources/colorfx/zigzagstyles.h new file mode 100644 index 0000000..25e1210 --- /dev/null +++ b/toonz/sources/colorfx/zigzagstyles.h @@ -0,0 +1,75 @@ + + +#ifndef ZIGZAG_STROKE_STYLE_H +#define ZIGZAG_STROKE_STYLE_H + +#include "tpixel.h" + +#ifdef POICIPENSO + +class TZigzagStrokeStyle : public TStrokeStyle +{ + TPixel32 m_color; + double m_density; + +public: + TZigzagStrokeStyle(const TPixel32 &color); + TZigzagStrokeStyle(); + + void invalidate() {} + + void changeParameter(double delta); + TStrokeStyle *clone() const; + + void draw(double pixelSize, const TColorFunction * = 0); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_density; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_density; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 115; }; + bool operator==(const TStrokeStyle &style) const + { + if (getTagId() != style.getTagId()) + return false; + return m_color == ((TZigzagStrokeStyle &)style).m_color && + m_density == ((TZigzagStrokeStyle &)style).m_density; + } +}; + +class TImageBasedZigzagStrokeStyle : public TStrokeStyle +{ + TPixel32 m_color; + double m_textScale; + TRaster32P m_texture; + +public: + TImageBasedZigzagStrokeStyle(const TPixel32 &color); + TImageBasedZigzagStrokeStyle(); + TImageBasedZigzagStrokeStyle(const TRaster32P texture); + + void invalidate() {} + inline int realTextCoord(const int a, const int l) const; + + void changeParameter(double delta); + TStrokeStyle *clone() const; + + void draw(double pixelSize, const TColorFunction * = 0); + + void loadData(TInputStreamInterface &is) { is >> m_color >> m_textScale; } + void saveData(TOutputStreamInterface &os) const { os << m_color << m_textScale; } + bool isSaveSupported() { return true; } + + int getTagId() const { return 116; }; + bool operator==(const TStrokeStyle &style) const + { + if (getTagId() != style.getTagId()) + return false; + return m_color == ((TImageBasedZigzagStrokeStyle &)style).m_color && + m_textScale == ((TImageBasedZigzagStrokeStyle &)style).m_textScale; + } +}; + +#endif + +#endif diff --git a/toonz/sources/common/expressions/texpression.cpp b/toonz/sources/common/expressions/texpression.cpp new file mode 100644 index 0000000..04f9303 --- /dev/null +++ b/toonz/sources/common/expressions/texpression.cpp @@ -0,0 +1,213 @@ + + +// TnzBase includes +#include "tgrammar.h" +#include "tparser.h" +#include "tunit.h" + +#include "texpression.h" + +using namespace TSyntax; + +//********************************************************************************** +// TExpression::Imp definition +//********************************************************************************** + +class TExpression::Imp +{ +public: + const Grammar *m_grammar; //!< (not owned) The expression's grammar + TDoubleParam *m_param; //!< (not owned) The expression's owner + Calculator *m_calculator; //!< (owned) Expression calculator object + + std::string m_text; //!< Text content + + std::string m_error; //!< Description of an error in the expression's text + std::pair m_errorPos; //!< Position of the error in the expression's text + + bool m_isValid, //!< Whether the expression is valid + m_hasBeenParsed; //!< Whether the expression has already been parsed + +public: + Imp() : m_grammar(0), m_param(0), m_calculator(0), m_errorPos(0, -1), m_isValid(false), m_hasBeenParsed(true) {} + ~Imp() { delete m_calculator; } +}; + +//********************************************************************************** +// TExpression implementation +//********************************************************************************** + +TExpression::TExpression() + : m_imp(new Imp()) +{ +} + +//-------------------------------------------------------------------------- + +TExpression::~TExpression() +{ + delete m_imp; +} + +//-------------------------------------------------------------------------- + +TExpression::TExpression(const TExpression &src) + : m_imp(new Imp()) +{ + m_imp->m_grammar = src.m_imp->m_grammar; + m_imp->m_param = src.m_imp->m_param; + m_imp->m_text = src.m_imp->m_text; + m_imp->m_calculator = 0; + m_imp->m_isValid = src.m_imp->m_isValid; + m_imp->m_error = src.m_imp->m_error; + m_imp->m_errorPos = src.m_imp->m_errorPos; + m_imp->m_hasBeenParsed = false; +} + +//-------------------------------------------------------------------------- + +TExpression &TExpression::operator=(TExpression copy) +{ + std::swap(copy.m_imp, m_imp); + return *this; +} + +//-------------------------------------------------------------------------- + +void TExpression::setGrammar(const TSyntax::Grammar *grammar) +{ + m_imp->m_grammar = grammar; + m_imp->m_hasBeenParsed = false; +} + +//-------------------------------------------------------------------------- + +const TSyntax::Grammar *TExpression::getGrammar() const +{ + return m_imp->m_grammar; +} + +//-------------------------------------------------------------------------- + +void TExpression::setText(string text) +{ + if (m_imp->m_text != text) { + m_imp->m_text = text; + delete m_imp->m_calculator; + m_imp->m_calculator = 0; + m_imp->m_isValid = false; + m_imp->m_hasBeenParsed = false; + m_imp->m_error = ""; + m_imp->m_errorPos = std::make_pair(0, -1); + } +} + +//-------------------------------------------------------------------------- + +string TExpression::getText() const +{ + return m_imp->m_text; +} + +//-------------------------------------------------------------------------- + +// note: unit is used by the CycleNode node only +TSyntax::Calculator *TExpression::getCalculator() +{ + if (!m_imp->m_hasBeenParsed) + parse(); + if (m_imp->m_isValid && m_imp->m_calculator) + return m_imp->m_calculator; + else + return 0; +} + +//-------------------------------------------------------------------------- + +bool TExpression::isValid() +{ + if (!m_imp->m_hasBeenParsed) + parse(); + return m_imp->m_isValid; +} + +//-------------------------------------------------------------------------- + +string TExpression::getError() const +{ + return m_imp->m_error; +} + +//-------------------------------------------------------------------------- + +std::pair TExpression::getErrorPos() const +{ + return m_imp->m_errorPos; +} + +//-------------------------------------------------------------------------- + +void TExpression::accept(TSyntax::CalculatorNodeVisitor &visitor) +{ + if (!m_imp->m_hasBeenParsed) + parse(); + if (m_imp->m_isValid && m_imp->m_calculator) + m_imp->m_calculator->accept(visitor); +} + +//-------------------------------------------------------------------------- + +void TExpression::parse() +{ + delete m_imp->m_calculator; + m_imp->m_calculator = 0; + + m_imp->m_errorPos = std::make_pair(0, -1); + m_imp->m_error = std::string(); + + if (!m_imp->m_grammar) { + m_imp->m_error = "No grammar defined"; + m_imp->m_isValid = false; + } else { + Parser parser(m_imp->m_grammar); + + m_imp->m_calculator = parser.parse(m_imp->m_text); + + if (m_imp->m_calculator) + m_imp->m_calculator->setOwnerParameter(m_imp->m_param); + + m_imp->m_isValid = parser.isValid(); + + if (!m_imp->m_isValid) { + m_imp->m_error = parser.getError(); + m_imp->m_errorPos = parser.getErrorPos(); + } + } + + m_imp->m_hasBeenParsed = true; +} + +//-------------------------------------------------------------------------- + +void TExpression::setOwnerParameter(TDoubleParam *param) +{ + m_imp->m_param = param; + + if (m_imp->m_calculator) + m_imp->m_calculator->setOwnerParameter(param); +} + +//-------------------------------------------------------------------------- + +TDoubleParam *TExpression::getOwnerParameter() const +{ + return m_imp->m_param; +} + +//-------------------------------------------------------------------------- + +bool TExpression::isCycling() const +{ + // TODO: this is a quick&dirty implementation to be replaced with a more "semantic" one + return getText().find("cycle") != string::npos; +} diff --git a/toonz/sources/common/expressions/tgrammar.cpp b/toonz/sources/common/expressions/tgrammar.cpp new file mode 100644 index 0000000..6d5de6b --- /dev/null +++ b/toonz/sources/common/expressions/tgrammar.cpp @@ -0,0 +1,1353 @@ + + +// TnzCore includes +#include "trandom.h" +#include "tutil.h" +#include "tconvert.h" + +// TnzBase includes +#include "ttokenizer.h" +#include "tunit.h" +#include "tparser.h" +#include "tdoubleparam.h" +#include "tdoublekeyframe.h" + +// STD includes +#include +#include +#include + +// Qt includes +#include + +#include "tgrammar.h" + +const double PI = 4 * atan(1.0); +const double toDeg(double rad) { return rad * 180.0 / PI; } +const double toRad(double deg) { return deg / 180.0 * PI; } + +namespace TSyntax +{ + +//=================================================================== +// +// RandomManager +// +// es. x = RandomManager::instance()->getValue(seed, frame); +// +//------------------------------------------------------------------- + +class RandomSequence +{ + TRandom m_rnd; + std::vector m_values; + +public: + RandomSequence(UINT seed) : m_rnd(seed) {} + double getValue(int i) + { + assert(i >= 0); + if (i >= (int)m_values.size()) { + m_values.reserve(i + 1); + while (i >= (int)m_values.size()) + m_values.push_back(m_rnd.getDouble()); + } + return m_values[i]; + } +}; + +//------------------------------------------------------------------- + +class RandomManager +{ // singleton + typedef std::map Table; + Table m_table; + RandomManager() {} +public: + ~RandomManager() + { + for (Table::iterator i = m_table.begin(); i != m_table.end(); ++i) + delete i->second; + } + static RandomManager *instance() + { + static RandomManager _instance; + return &_instance; + } + + RandomSequence *getSequence(UINT seed) + { + Table::iterator i = m_table.find(seed); + if (i == m_table.end()) { + RandomSequence *seq = new RandomSequence(seed); + m_table[seed] = seq; + return seq; + } else + return i->second; + } + + double getValue(UINT seed, double t) + { + RandomSequence *seq = getSequence(seed); + return seq->getValue(t > 0 ? tfloor(t) : 0); + } +}; + +//=================================================================== +// Calculator +//------------------------------------------------------------------- + +Calculator::Calculator() + : m_rootNode(0), m_param(0), m_unit(0) +{ +} + +//------------------------------------------------------------------- + +Calculator::~Calculator() +{ + delete m_rootNode; +} + +//------------------------------------------------------------------- + +void Calculator::setRootNode(CalculatorNode *node) +{ + if (node != m_rootNode) { + delete m_rootNode; + m_rootNode = node; + } +} + +//=================================================================== +// Nodes +//------------------------------------------------------------------- + +template +class Op0Node : public CalculatorNode +{ +public: + Op0Node(Calculator *calc) + : CalculatorNode(calc) {} + + double compute(double vars[3]) const + { + Op op; + return op(); + } +}; + +//------------------------------------------------------------------- + +template +class Op1Node : public CalculatorNode +{ +protected: + std::auto_ptr m_a; + +public: + Op1Node(Calculator *calc, CalculatorNode *a) + : CalculatorNode(calc), m_a(a) {} + + double compute(double vars[3]) const + { + Op op; + return op(m_a->compute(vars)); + } + + void accept(CalculatorNodeVisitor &visitor) { m_a->accept(visitor); } +}; + +//------------------------------------------------------------------- + +template +class Op2Node : public CalculatorNode +{ +protected: + std::auto_ptr m_a, m_b; + +public: + Op2Node(Calculator *calc, CalculatorNode *a, CalculatorNode *b) + : CalculatorNode(calc), m_a(a), m_b(b) {} + + double compute(double vars[3]) const + { + Op op; + return op(m_a->compute(vars), m_b->compute(vars)); + } + + void accept(CalculatorNodeVisitor &visitor) + { + m_a->accept(visitor), m_b->accept(visitor); + } +}; + +//------------------------------------------------------------------- + +template +class Op3Node : public CalculatorNode +{ +protected: + std::auto_ptr m_a, m_b, m_c; + +public: + Op3Node(Calculator *calc, CalculatorNode *a, CalculatorNode *b, CalculatorNode *c) + : CalculatorNode(calc), m_a(a), m_b(b), m_c(c) {} + + double compute(double vars[3]) const + { + Op op; + return op(m_a->compute(vars), m_b->compute(vars), m_c->compute(vars)); + } + + void accept(CalculatorNodeVisitor &visitor) + { + m_a->accept(visitor), m_b->accept(visitor), m_c->accept(visitor); + } +}; + +//------------------------------------------------------------------- + +class ChsNode : public CalculatorNode +{ + std::auto_ptr m_a; + +public: + ChsNode(Calculator *calc, CalculatorNode *a) : CalculatorNode(calc), m_a(a) {} + + double compute(double vars[3]) const { return -m_a->compute(vars); } + void accept(CalculatorNodeVisitor &visitor) { m_a->accept(visitor); } +}; + +//------------------------------------------------------------------- + +class QuestionNode : public CalculatorNode +{ + std::auto_ptr m_a, m_b, m_c; + +public: + QuestionNode(Calculator *calc, CalculatorNode *a, CalculatorNode *b, CalculatorNode *c) + : CalculatorNode(calc), m_a(a), m_b(b), m_c(c) {} + + double compute(double vars[3]) const + { + return (m_a->compute(vars) != 0) ? m_b->compute(vars) : m_c->compute(vars); + } + + void accept(CalculatorNodeVisitor &visitor) + { + m_a->accept(visitor), m_b->accept(visitor), m_c->accept(visitor); + } +}; + +//------------------------------------------------------------------- + +class NotNode : public CalculatorNode +{ + std::auto_ptr m_a; + +public: + NotNode(Calculator *calc, CalculatorNode *a) : CalculatorNode(calc), m_a(a) {} + + double compute(double vars[3]) const { return m_a->compute(vars) == 0; } + void accept(CalculatorNodeVisitor &visitor) { m_a->accept(visitor); } +}; +//------------------------------------------------------------------- + +class CycleNode : public CalculatorNode +{ + std::auto_ptr m_a; + +public: + CycleNode(Calculator *calc, CalculatorNode *a) : CalculatorNode(calc), m_a(a) {} + + double compute(double vars[3]) const + { + struct locals { + static inline double compute(const TDoubleParam ¶m, double f) + { + if (param.getKeyframeCount() >= 2 && f < param.keyframeIndexToFrame(0)) { + TDoubleKeyframe kf = param.getKeyframe(0); + if (kf.m_type == TDoubleKeyframe::Expression || kf.m_type == TDoubleKeyframe::SimilarShape) + return param.getDefaultValue(); + } + + double value = param.getValue(f); + return value; + } + }; + + double delta = tmax(1.0, m_a->compute(vars)); + + const TDoubleParam *ownerParam = getCalculator()->getOwnerParameter(); + if (!ownerParam) + return 0; + + double value = locals::compute(*ownerParam, vars[FRAME] - 1 - delta); + if (getCalculator()->getUnit()) + value = getCalculator()->getUnit()->convertTo(value); + + return value; + } + + void accept(CalculatorNodeVisitor &visitor) { m_a->accept(visitor); } +}; + +//------------------------------------------------------------------- + +class RandomNode : public CalculatorNode +{ + std::auto_ptr m_seed, m_min, m_max, m_arg; + +public: + RandomNode(Calculator *calc) : CalculatorNode(calc), m_seed(0), m_min(0), m_max(0) + { + m_arg.reset(new VariableNode(calc, CalculatorNode::FRAME)); + } + + void setSeed(CalculatorNode *arg) + { + assert(m_seed.get() == 0); + m_seed.reset(arg); + } + void setMax(CalculatorNode *arg) + { + assert(m_max.get() == 0); + m_max.reset(arg); + } + void setMin(CalculatorNode *arg) + { + assert(m_min.get() == 0); + m_min.reset(arg); + } + + double compute(double vars[3]) const + { + double s = (m_seed.get() != 0) ? m_seed->compute(vars) : 0; + double r = RandomManager::instance()->getValue(s, fabs(m_arg->compute(vars))); + if (m_min.get() == 0) + if (m_max.get() == 0) + return r; + else + return m_max->compute(vars) * r; + else + return (1 - r) * m_min->compute(vars) + r * m_max->compute(vars); + } + + void accept(CalculatorNodeVisitor &visitor) + { + m_arg->accept(visitor); + if (m_seed.get()) + m_seed->accept(visitor); + if (m_min.get()) + m_min->accept(visitor); + if (m_max.get()) + m_max->accept(visitor); + } +}; + +//=================================================================== +// Patterns +//------------------------------------------------------------------- + +CalculatorNode *Pattern::popNode(std::vector &stack) const +{ + CalculatorNode *node = stack.back(); + stack.pop_back(); + return node; +} + +//=================================================================== + +class NumberPattern : public Pattern +{ +public: + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getType() == Token::Number; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 1; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return Number; + } + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 1); + assert(tokens[0].getType() == Token::Number); + stack.push_back(new NumberNode(calc, tokens[0].getDoubleValue())); + } +}; + +//------------------------------------------------------------------- + +class ConstantPattern : public Pattern +{ + string m_constantName; + double m_value; + +public: + ConstantPattern(string constantName, double value, string description = "") + : m_constantName(constantName), m_value(value) + { + setDescription(description); + } + + string getFirstKeyword() const { return m_constantName; } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getText() == m_constantName; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 1; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return Constant; + } + + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 1); + stack.push_back(new NumberNode(calc, m_value)); + } +}; + +//------------------------------------------------------------------- + +class VariablePattern : public Pattern +{ + string m_variableName; + int m_varIdx; + +public: + VariablePattern(string variableName, int varIdx, string description = "") + : m_variableName(variableName), m_varIdx(varIdx) + { + setDescription(description); + } + + string getFirstKeyword() const { return m_variableName; } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getText() == m_variableName; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 1; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return Variable; + } + + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 1); + assert(tokens[0].getText() == m_variableName); + stack.push_back(new VariableNode(calc, m_varIdx)); + } +}; + +//------------------------------------------------------------------- + +template +class Op2Pattern : public Pattern +{ + string m_opName; + int m_priority; + +public: + Op2Pattern(string opName, int priority) + : m_opName(opName), m_priority(priority) {} + int getPriority() const { return m_priority; } + string getFirstKeyword() const { return m_opName; } + bool expressionExpected(const std::vector &previousTokens) const + { + return previousTokens.empty() || previousTokens.size() == 2; + } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 1 && token.getText() == m_opName; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 3; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 1 ? Operator : InternalError; + } + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 3); + assert(tokens[1].getText() == m_opName); + CalculatorNode *b = popNode(stack); + CalculatorNode *a = popNode(stack); + stack.push_back(new Op2Node(calc, a, b)); + } +}; + +//------------------------------------------------------------------- + +class UnaryMinusPattern : public Pattern +{ +public: + UnaryMinusPattern() {} + int getPriority() const { return 50; } + string getFirstKeyword() const { return "-"; } + + bool expressionExpected(const std::vector &previousTokens) const + { + return previousTokens.size() == 1; + } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getText() == "-"; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 2; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return Operator; + } + + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 2); + assert(tokens[0].getText() == "-"); + stack.push_back(new ChsNode(calc, popNode(stack))); + } +}; + +//------------------------------------------------------------------- + +class NotPattern : public Pattern +{ + string m_prefix; + +public: + NotPattern(string prefix, string description) : m_prefix(prefix) + { + setDescription(description); + } + int getPriority() const { return 5; } + string getFirstKeyword() const { return m_prefix; } + + bool expressionExpected(const std::vector &previousTokens) const + { + return previousTokens.size() == 1; + } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getText() == m_prefix; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 2; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return Operator; + } + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 2); + assert(tokens[0].getText() == m_prefix); + stack.push_back(new NotNode(calc, popNode(stack))); + } +}; + +//------------------------------------------------------------------- + +class QuestionTernaryPattern : public Pattern +{ +public: + QuestionTernaryPattern() {} + int getPriority() const { return 5; } + string getFirstKeyword() const { return "?"; } + + bool expressionExpected(const std::vector &previousTokens) const + { + int i = (int)previousTokens.size(); + return i == 0 || i == 2 || i == 4; + } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + int i = (int)previousTokens.size(); + return i == 1 && token.getText() == "?" || i == 3 && token.getText() == ":"; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 5; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + int i = (int)previousTokens.size(); + return (i == 1 || i == 3) ? Operator : InternalError; + } + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + CalculatorNode *node1 = popNode(stack); + CalculatorNode *node2 = popNode(stack); + CalculatorNode *node3 = popNode(stack); + stack.push_back(new QuestionNode(calc, node3, node2, node1)); + } +}; +//------------------------------------------------------------------- + +class BraketPattern : public Pattern +{ +public: + BraketPattern() {} + int getPriority() const { return 5; } + string getFirstKeyword() const { return "("; } + + bool expressionExpected(const std::vector &previousTokens) const + { + return previousTokens.size() == 1; + } + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.empty() && token.getText() == "(" || + previousTokens.size() == 2 && token.getText() == ")"; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() == 3; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + return previousTokens.size() != 1 ? Parenthesis : InternalError; + } + void createNode( + Calculator *calc, + std::vector &stack, + const std::vector &tokens) const + { + assert(tokens.size() == 3); + assert(tokens[0].getText() == "("); + assert(tokens[2].getText() == ")"); + } +}; + +//------------------------------------------------------------------- + +class FunctionPattern : public Pattern +{ +protected: + string m_functionName; + bool m_implicitArgAllowed; + // if m_implicitArgAllowed == true then the first argument is the frame number + // e.g. f(5) means f(frame,5) + // to use a different first argument (e.g. t*10) you have to write f(t*10;5) + + int m_minArgCount; + std::vector m_optionalArgDefaults; + +public: + FunctionPattern(string functionName, int minArgCount) + : m_functionName(functionName), m_implicitArgAllowed(false), m_minArgCount(minArgCount) + { + } + + void allowImplicitArg(bool allowed) { m_implicitArgAllowed = allowed; } + void addOptionalArg(double value) { m_optionalArgDefaults.push_back(value); } + + string getFirstKeyword() const { return m_functionName; } + bool expressionExpected(const std::vector &previousTokens) const + { + int n = (int)previousTokens.size(); + return 2 <= n && (n & 1) == 0; + } + + bool matchToken(const std::vector &previousTokens, const Token &token) const + { + int i = (int)previousTokens.size(); + string s = toLower(token.getText()); + if (i == 0) + return s == toLower(m_functionName); + else if (i == 1) + return s == "("; + else if ((i & 1) == 0) + return true; + else if (s == ",") + return true; + else if (s == ";") + return i == 3 && m_implicitArgAllowed; + else if (s == ")") { + int n = (i - 1) / 2; + if (previousTokens.size() > 3 && previousTokens[3].getText() == ";") + n--; + if (n < m_minArgCount || n > m_minArgCount + (int)m_optionalArgDefaults.size()) + return false; + else + return true; + } else + return false; + } + bool isFinished(const std::vector &previousTokens, const Token &token) const + { + if (previousTokens.empty()) + return false; + return m_minArgCount == 0 && previousTokens.size() == 1 && token.getText() != "(" || + previousTokens.back().getText() == ")"; + } + TokenType getTokenType(const std::vector &previousTokens, const Token &token) const + { + int i = (int)previousTokens.size(); + if (i == 0) + return Function; + else if (i == 1 || token.getText() == ")") + return Function; + else if (i == 3) + return token.getText() == ";" ? Comma : Comma; + else if (i & 1) + return Comma; + else + return InternalError; + } + + void getArgs( + std::vector &nodes, + Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + + bool implicitArgUsed = m_implicitArgAllowed && tokens.size() > 3 && tokens[3].getText() == ";"; + + // n = number of arguments to provide (mandatory + optional + implicit) + int n = m_minArgCount + (int)m_optionalArgDefaults.size() + (m_implicitArgAllowed ? 1 : 0); + + // m = number of default arguments to assign (with their default value) + int m = n - (tokens.size() - 2) / 2; + if (m_implicitArgAllowed && !implicitArgUsed) + m--; + + assert(m <= (int)m_optionalArgDefaults.size()); + if (m > (int)m_optionalArgDefaults.size()) + m = (int)m_optionalArgDefaults.size(); + + nodes.resize(n); + + // fetch arguments from the stack + int k = n - m; + if (implicitArgUsed) { + while (k > 0) + nodes[--k] = popNode(stack); + } else { + while (k > 1) + nodes[--k] = popNode(stack); + nodes[0] = new VariableNode(calc, CalculatorNode::FRAME); + } + + // add default values + for (int i = 0; i < m; i++) + nodes[n - m + i] = new NumberNode(calc, m_optionalArgDefaults[i]); + } +}; + +//------------------------------------------------------------------- + +template +class F0Pattern : public FunctionPattern +{ +public: + F0Pattern(string functionName) : FunctionPattern(functionName, 0) {} + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + stack.push_back(new Op0Node(calc, m_functionName)); + } +}; + +//------------------------------------------------------------------- + +template +class F1Pattern : public FunctionPattern +{ +public: + F1Pattern(string functionName, string descr = "") : FunctionPattern(functionName, 1) { setDescription(descr); } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + stack.push_back(new Op1Node(calc, popNode(stack))); + } +}; + +//------------------------------------------------------------------- + +template +class F2Pattern : public FunctionPattern +{ +public: + F2Pattern(string functionName, string descr = "") : FunctionPattern(functionName, 2) { setDescription(descr); } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + CalculatorNode *b = popNode(stack); + CalculatorNode *a = popNode(stack); + stack.push_back(new Op2Node(calc, a, b)); + } +}; + +//------------------------------------------------------------------- + +template +class F3Pattern : public FunctionPattern +{ +public: + F3Pattern(string functionName, string descr = "") : FunctionPattern(functionName, 3) { setDescription(descr); } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + CalculatorNode *c = popNode(stack); + CalculatorNode *b = popNode(stack); + CalculatorNode *a = popNode(stack); + stack.push_back(new Op3Node(calc, a, b, c)); + } +}; + +//------------------------------------------------------------------- + +template +class Fs2Pattern : public FunctionPattern +{ +public: + Fs2Pattern(string functionName, string description) + : FunctionPattern(functionName, 1) + { + allowImplicitArg(true); + setDescription(description); + } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + + std::vector nodes; + getArgs(nodes, calc, stack, tokens); + stack.push_back(new Op2Node(calc, nodes[0], nodes[1])); + } +}; + +//------------------------------------------------------------------- + +template +class Fs3Pattern : public FunctionPattern +{ +public: + Fs3Pattern(string functionName, double defVal, string descr) + : FunctionPattern(functionName, 1) + { + allowImplicitArg(true); + addOptionalArg(defVal); + setDescription(descr); + } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + + std::vector nodes; + getArgs(nodes, calc, stack, tokens); + stack.push_back(new Op3Node(calc, nodes[0], nodes[1], nodes[2])); + } +}; + +//------------------------------------------------------------------- + +class CyclePattern : public FunctionPattern +{ +public: + CyclePattern(string functionName) : FunctionPattern(functionName, 1) + { + setDescription("cycle(period)\nCycles the transitions of the period previous frames to the selected range"); + } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + CalculatorNode *a = popNode(stack); + stack.push_back(new CycleNode(calc, a)); + } +}; + +//------------------------------------------------------------------- + +class RandomPattern : public FunctionPattern +{ + bool m_seed; + +public: + RandomPattern(string functionName, bool seed, string description) + : FunctionPattern(functionName, seed ? 1 : 0), m_seed(seed) + { + allowImplicitArg(true); + addOptionalArg(0); + addOptionalArg(0); + setDescription(description); + } + void createNode(Calculator *calc, std::vector &stack, + const std::vector &tokens) const + { + int n = ((int)tokens.size() - 1) / 2; + if (m_seed) + n--; + RandomNode *randomNode = new RandomNode(calc); + if (n > 0) { + randomNode->setMax(popNode(stack)); + if (n > 1) + randomNode->setMin(popNode(stack)); + } + if (m_seed) + randomNode->setSeed(popNode(stack)); + stack.push_back(randomNode); + } +}; + +//=================================================================== + +class PatternTable +{ + std::map m_kTable; + std::vector m_uTable; + Grammar::Position m_position; + +public: + PatternTable(Grammar::Position position) : m_position(position) {} + + ~PatternTable() + { + for (std::map::iterator + it = m_kTable.begin(); + it != m_kTable.end(); ++it) + delete it->second; + for (std::vector::iterator + it = m_uTable.begin(); + it != m_uTable.end(); ++it) + delete *it; + } + + void addPattern(Pattern *pattern) + { + string keyword = pattern->getFirstKeyword(); + if (keyword != "") { + // first keyword should be unique + assert(m_kTable.count(keyword) == 0); + m_kTable[keyword] = pattern; + } else + m_uTable.push_back(pattern); + } + + const Pattern *getPattern(const Token &token) const + { + std::vector tokens; + if (m_position == Grammar::ExpressionEnd) + tokens.push_back(Token()); + if (token.getType() == Token::Punct || token.getType() == Token::Ident) { + string keyword = token.getText(); + std::map::const_iterator it = m_kTable.find(keyword); + if (it != m_kTable.end()) { + Pattern *pattern = it->second; + if (pattern->matchToken(tokens, token)) { + return pattern; + } + } + } + for (int i = 0; i < (int)m_uTable.size(); i++) { + Pattern *pattern = m_uTable[i]; + if (pattern->matchToken(tokens, token)) { + return pattern; + } + } + return 0; + } + + void getSuggestions(Grammar::Suggestions &suggestions) const + { + std::map::const_iterator it; + for (it = m_kTable.begin(); it != m_kTable.end(); ++it) { + suggestions.push_back(std::make_pair(it->first, it->second->getDescription())); + } + for (int i = 0; i < (int)m_uTable.size(); i++) { + std::vector keywords; + m_uTable[i]->getAcceptableKeywords(keywords); + for (int j = 0; j < (int)keywords.size(); j++) + suggestions.push_back(std::make_pair(keywords[j], m_uTable[i]->getDescription())); + } + } +}; + +//=================================================================== +// +// funzioni trigonometriche, log, exp, ecc. +// +//------------------------------------------------------------------- + +class Pow +{ +public: + double operator()(double x, double y) const { return pow(x, y); } +}; +// class Mod {public: double operator()(double x, double y) const {return (int)x % (int)y;} }; +class Sin +{ +public: + double operator()(double x) const { return sin(toRad(x)); } +}; +class Cos +{ +public: + double operator()(double x) const { return cos(toRad(x)); } +}; +class Tan +{ +public: + double operator()(double x) const { return tan(toRad(x)); } +}; +class Sinh +{ +public: + double operator()(double x) const { return sinh(toRad(x)); } +}; +class Cosh +{ +public: + double operator()(double x) const { return cosh(toRad(x)); } +}; +class Tanh +{ +public: + double operator()(double x) const { return tanh(toRad(x)); } +}; +class Atan +{ +public: + double operator()(double x) const { return toDeg(atan(x)); } +}; +class Atan2 +{ +public: + double operator()(double x, double y) const { return toDeg(atan2(x, y)); } +}; +class Log +{ +public: + double operator()(double x) const { return log(x); } +}; +class Exp +{ +public: + double operator()(double x) const { return exp(x); } +}; +class Floor +{ +public: + double operator()(double x) const { return tfloor(x); } +}; +class Ceil +{ +public: + double operator()(double x) const { return tceil(x); } +}; +class Round +{ +public: + double operator()(double x) const { return tround(x); } +}; +class Abs +{ +public: + double operator()(double x) const { return fabs(x); } +}; +class Sign +{ +public: + double operator()(double x) const { return x > 0 ? 1 : x < 0 ? -1 : 0; } +}; +class Sqrt +{ +public: + double operator()(double x) const { return x >= 0.0 ? sqrt(x) : 0; } +}; +class Sqr +{ +public: + double operator()(double x) const { return x * x; } +}; +class Crop +{ +public: + double operator()(double x, double a, double b) const { return tcrop(x, a, b); } +}; +class Step +{ +public: + double operator()(double x, double y) const { return x < y ? 0 : 1; } +}; + +class Mod +{ +public: + double operator()(double x, double y) const + { + if (y == 0.0) + return 0; + return x - y * floor(x / y); + } +}; + +class Min +{ +public: + double operator()(double x, double y) const { return x < y ? x : y; } +}; +class Max +{ +public: + double operator()(double x, double y) const { return x > y ? x : y; } +}; + +class Gt +{ +public: + double operator()(double x, double y) const { return x > y ? 1 : 0; } +}; +class Ge +{ +public: + double operator()(double x, double y) const { return x >= y ? 1 : 0; } +}; +class Lt +{ +public: + double operator()(double x, double y) const { return x < y ? 1 : 0; } +}; +class Le +{ +public: + double operator()(double x, double y) const { return x <= y ? 1 : 0; } +}; +class Ne +{ +public: + double operator()(double x, double y) const { return x != y ? 1 : 0; } +}; +class Eq +{ +public: + double operator()(double x, double y) const { return x == y ? 1 : 0; } +}; +class And +{ +public: + double operator()(double x, double y) const { return (x != 0) && (y != 0) ? 1 : 0; } +}; +class Or +{ +public: + double operator()(double x, double y) const { return (x != 0) || (y != 0) ? 1 : 0; } +}; +class Not +{ +public: + double operator()(double x) const { return x == 0; } +}; + +class Smoothstep +{ +public: + double operator()(double v, double min, double max) const + { + if (v <= min) + return 0; + else if (v >= max) + return 1; + double t = (v - min) / (max - min); + return -2 * t * t * t + 3 * t * t; + } +}; +class Pulse +{ +public: + double operator()(double x, double x0, double length) const + { + // double length=5.0; + double b = (.69315 * 4.0) / (length * length); + x -= x0; + return exp(-x * x * b); + } +}; +class Saw +{ +public: + double operator()(double x, double length, double height) const + { + if (length <= 0.0) + return 0.0; + if (height <= 0.0) + height = length; + double q = x / length; + return height * (q - floor(q)); + } +}; +class Wave +{ +public: + double operator()(double x, double length) const + { + if (length <= 0.0) + return 0.0; + return sin(x * 2 * PI / length); + } +}; + +//=================================================================== + +class Grammar::Imp +{ +public: + PatternTable m_prePatterns, m_postPatterns; + + Imp() + : m_prePatterns(Grammar::ExpressionStart), m_postPatterns(Grammar::ExpressionEnd) + { + } + ~Imp() {} +}; + +Grammar::Grammar() + : m_imp(new Imp()) +{ + addPattern(new NumberPattern()); + addPattern(new ConstantPattern("pi", PI, "3.14159265...")); + addPattern(new VariablePattern("t", CalculatorNode::T, "ranges from 0.0 to 1.0 along the transition")); + const string f_desc = "the current frame number"; + addPattern(new VariablePattern("f", CalculatorNode::FRAME, f_desc)); + addPattern(new VariablePattern("frame", CalculatorNode::FRAME, f_desc)); + const string r_desc = "the current frame number, relative to the transition"; + addPattern(new VariablePattern("r", CalculatorNode::RFRAME, r_desc)); + addPattern(new VariablePattern("rframe", CalculatorNode::RFRAME, r_desc)); + addPattern(new Op2Pattern>("+", 10)); + addPattern(new Op2Pattern>("*", 20)); + addPattern(new Op2Pattern("^", 30)); + addPattern(new Op2Pattern>("-", 10)); + addPattern(new Op2Pattern>("/", 20)); + addPattern(new Op2Pattern("%", 8)); + addPattern(new Op2Pattern(">", 6)); + addPattern(new Op2Pattern(">=", 6)); + addPattern(new Op2Pattern("<", 6)); + addPattern(new Op2Pattern("<=", 6)); + addPattern(new Op2Pattern("==", 6)); + addPattern(new Op2Pattern("!=", 6)); + addPattern(new Op2Pattern("&&", 3)); + addPattern(new Op2Pattern("||", 2)); + addPattern(new Op2Pattern("and", 3)); + addPattern(new Op2Pattern("or", 2)); + addPattern(new NotPattern("!", "not")); + addPattern(new NotPattern("not", "")); + addPattern(new UnaryMinusPattern()); + addPattern(new BraketPattern()); + addPattern(new QuestionTernaryPattern()); + addPattern(new F1Pattern("sin", "sin(degree)")); + addPattern(new F1Pattern("cos", "cos(degree)")); + addPattern(new F1Pattern("tan", "tan(degree)")); + addPattern(new F1Pattern("sinh", "sinh(degree)\nHyperbolic sine")); + addPattern(new F1Pattern("cosh", "cosh(degree)\nHyperbolic cosine")); + addPattern(new F1Pattern("tanh", "tanh(degree)\nHyperbolic tangent")); + addPattern(new F1Pattern("atan", "atan(x)\nArctangent : the inverse of tan()")); + addPattern(new F2Pattern("atan2", "atan2(y,x)\nThe counter-clockwise angle in degree between the x-axis and the point(x,y)")); + addPattern(new F1Pattern("log", "log(x)\nThe natural logarithm of x (base e)")); + addPattern(new F1Pattern("exp", "exp(x)\nThe base-e exponential of x")); + addPattern(new F1Pattern("floor", "floor(x)\nThe greatest integer <= x")); + const string ceil_desc = "The smallest integer >= x"; + addPattern(new F1Pattern("ceil", "ceil(x)\n" + ceil_desc)); + addPattern(new F1Pattern("ceiling", "ceiling(x)\n" + ceil_desc)); + addPattern(new F1Pattern("round", "round(x)\nThe integer nearest to x")); + addPattern(new F1Pattern("abs", "abs(x)\nThe absolute value of x")); + addPattern(new F1Pattern("sign", "sign(x)\n-1 if x<0, 1 if x>0 and 0 if x=0")); + const string sqrt_desc = "Square root of x"; + addPattern(new F1Pattern("sqrt", "sqrt(x)\n" + sqrt_desc)); + addPattern(new F1Pattern("sqr", "sqr(x)\n" + sqrt_desc)); + addPattern(new F3Pattern("crop", "crop(x,a,b)\na if xb, x if x in [a,b]")); + addPattern(new F3Pattern("clamp", "clamp(x,a,b)\na if xb, x if x in [a,b]")); + addPattern(new F2Pattern("min", "min(a,b)")); + addPattern(new F2Pattern("max", "max(a,b)")); + addPattern(new F2Pattern("step", "min(x,x0)\n0 if x=x0")); + addPattern(new F3Pattern("smoothstep", "smoothstep(x,x0)\n0 if x=x0\nas step, but with smooth transition")); + const string pulse_desc = "Generates a bump ranging from 0.0 to 1.0 set at position pos"; + addPattern(new Fs3Pattern("pulse", 0.5, "pulse(pos)\npulse(pos,length)\npulse(arg; pos)\npulse(arg;pos,length)\n" + pulse_desc)); + addPattern(new Fs3Pattern("bump", 0.5, "bump(pos)\nbump(pos,length)\nbump(arg; pos)\nbump(arg;pos,length)\n" + pulse_desc)); + const string saw_desc = "Generates a periodic sawtooth shaped curve"; + addPattern(new Fs3Pattern("sawtooth", 0.0, "sawtooth(length)\nsawtooth(length, height)\nsawtooth(arg; length)\nsawtooth(arg; length, height)\n" + saw_desc)); + addPattern(new Fs3Pattern("saw", 0.0, "saw(length)\nsaw(length, height)\nsaw(arg; length)\nsaw(arg; length, height)\n" + saw_desc)); + addPattern(new Fs2Pattern("wave", "wave(_length)\nwave(_arg;_length)\nsame as sin(f*180/length)")); + const string rnd_desc = "Generates random number between min and max"; + addPattern(new RandomPattern("random", false, "random = random(0,1)\nrandom(max) = random(0,max)\nrandom(min,max)\n" + rnd_desc)); + addPattern(new RandomPattern("rnd", false, "rnd = rnd(0,1)\nrnd(max) = rnd(0,max)\nrnd(min,max)\n" + rnd_desc)); + const string rnd_s_desc = rnd_desc + "; seed select different random sequences"; + addPattern(new RandomPattern("random_s", true, "random_s(seed) = random_s(seed, 0,1)\nrandom_s(seed,max) = random_s(seed, 0,max)\nrandom_s(seed,min,max)\n" + rnd_s_desc)); + addPattern(new RandomPattern("rnd_s", true, "rnd_s(seed) = rnd_s(seed, 0,1)\nrnd_s(seed,max) = rnd_s(seed, 0,max)\nrnd_s(seed,min,max)\n" + rnd_s_desc)); + + addPattern(new CyclePattern("cycle")); +} + +Grammar::~Grammar() +{ + delete m_imp; +} + +void Grammar::addPattern(Pattern *pattern) +{ + std::vector noTokens; + if (pattern->expressionExpected(noTokens)) + m_imp->m_postPatterns.addPattern(pattern); + else + m_imp->m_prePatterns.addPattern(pattern); +} + +const Pattern *Grammar::getPattern(Position position, const Token &token) const +{ + Pattern *pattern = 0; + if (position == ExpressionStart) + return m_imp->m_prePatterns.getPattern(token); + else + return m_imp->m_postPatterns.getPattern(token); +} + +void Grammar::getSuggestions(Grammar::Suggestions &suggestions, Position position) const +{ + if (position == ExpressionStart) + return m_imp->m_prePatterns.getSuggestions(suggestions); + else + return m_imp->m_postPatterns.getSuggestions(suggestions); +} + +//=================================================================== + +} // namespace TSyntax diff --git a/toonz/sources/common/expressions/tparser.cpp b/toonz/sources/common/expressions/tparser.cpp new file mode 100644 index 0000000..aecc98d --- /dev/null +++ b/toonz/sources/common/expressions/tparser.cpp @@ -0,0 +1,351 @@ + + +#include "tparser.h" +#include "tgrammar.h" +#include "ttokenizer.h" +#include "tutil.h" + +#include + +namespace TSyntax +{ + +//------------------------------------------------------------------- +// RunningPattern +//------------------------------------------------------------------- + +class RunningPattern +{ +public: + std::vector m_tokens; + const Pattern *m_pattern; + + RunningPattern(const Pattern *pattern) : m_pattern(pattern) {} + + int getPriority() const { return m_pattern->getPriority(); } + bool isFinished(const Token &token) const { return m_pattern->isFinished(m_tokens, token); } + bool isComplete(const Token &token) const { return m_pattern->isComplete(m_tokens, token); } + bool expressionExpected() const { return m_pattern->expressionExpected(m_tokens); } + bool matchToken(const Token &token) const { return m_pattern->matchToken(m_tokens, token); } + void advance() + { + assert(!isFinished(Token())); + m_tokens.push_back(Token()); + } + void advance(const Token &token) + { + assert(!isFinished(token)); + m_tokens.push_back(token); + } + TokenType getTokenType(const Token &token) const { return m_pattern->getTokenType(m_tokens, token); } + + void createNode(Calculator *calc, std::vector &stack) + { + m_pattern->createNode(calc, stack, m_tokens); + } +}; + +//------------------------------------------------------------------- +// Parser::Imp +//------------------------------------------------------------------- + +class Parser::Imp +{ +public: + const Grammar *m_grammar; + Tokenizer m_tokenizer; + string m_errorString; + bool m_isValid; + Calculator *m_calculator; + std::vector m_patternStack; + std::vector m_nodeStack; + std::vector m_syntaxTokens; + Grammar::Position m_position; + // Pattern *m_lastPattern; + + Imp(const Grammar *grammar) + : m_grammar(grammar), m_errorString(""), m_isValid(false), m_calculator(0), m_position(Grammar::ExpressionStart) + { + } + ~Imp() + { + clearPointerContainer(m_nodeStack); + delete m_calculator; + } + + void pushSyntaxToken(TokenType type); + bool parseExpression(bool checkOnly); + void flushPatterns(int minPriority, int minIndex, bool checkOnly); + bool checkSyntax(std::vector &tokens); +}; + +//------------------------------------------------------------------- +// Parser +//------------------------------------------------------------------- + +Parser::Parser(const Grammar *grammar) + : m_imp(new Imp(grammar)) +{ +} + +//------------------------------------------------------------------- + +Parser::~Parser() +{ + delete m_imp; +} + +//------------------------------------------------------------------- + +void Parser::Imp::flushPatterns(int minPriority, int minIndex, bool checkOnly) +{ + while ((int)m_patternStack.size() > minIndex && m_patternStack.back().getPriority() >= minPriority) { + if (!checkOnly) + m_patternStack.back().createNode(m_calculator, m_nodeStack); + m_patternStack.pop_back(); + } +} + +//------------------------------------------------------------------- + +void Parser::Imp::pushSyntaxToken(TokenType type) +{ + SyntaxToken syntaxToken; + Token token = m_tokenizer.getToken(); + syntaxToken.m_pos = token.getPos(); + syntaxToken.m_length = token.getPos1() - token.getPos() + 1; + syntaxToken.m_type = type; + m_syntaxTokens.push_back(syntaxToken); +} + +//------------------------------------------------------------------- + +bool Parser::Imp::parseExpression(bool checkOnly) +{ + if (!m_grammar) + return false; + int stackMinIndex = m_patternStack.size(); + int count = 0; + Grammar::Position position = Grammar::ExpressionStart; + m_position = position; + // warning: parseExpression() is called recursively. m_position is used after parseExpression() + // for diagnostic and suggestions. position is used in the actual parsing + + bool expressionExpected = true; + while (!m_tokenizer.eos()) { + // token, position -> pattern + const Pattern *pattern = m_grammar->getPattern(position, m_tokenizer.getToken()); + if (!pattern) { + // no pattern found for the next token + if (position == Grammar::ExpressionEnd) { + // already found an expression + flushPatterns(-1, stackMinIndex, checkOnly); + return true; + } else { + // syntax error : expression not found + if (checkOnly) + pushSyntaxToken(UnexpectedToken); + + m_errorString = "Unexpected token"; + return false; + } + } + // pattern found: start scanning it + RunningPattern runningPattern(pattern); + if (position == Grammar::ExpressionEnd) // patterns of the form " ...." + runningPattern.advance(); + + // eat the first token + if (checkOnly) + pushSyntaxToken(runningPattern.getTokenType(m_tokenizer.getToken())); + runningPattern.advance(m_tokenizer.getToken()); + m_tokenizer.nextToken(); + expressionExpected = false; + + while (!runningPattern.isFinished(m_tokenizer.getToken())) { + if (runningPattern.expressionExpected()) { + // expression expected + runningPattern.advance(); + if (runningPattern.isFinished(m_tokenizer.getToken())) { + // pattern ended with an expression + expressionExpected = true; + break; + } + // parse a sub expression + if (!parseExpression(checkOnly)) { + return false; + } + } else { + // "terminal" expected + if (m_tokenizer.eos()) { + // EOS + if (runningPattern.isComplete(Token())) + break; + if (checkOnly) + pushSyntaxToken(Eos); + + m_errorString = "Uncompleted syntax"; + return false; + } + if (!runningPattern.matchToken(m_tokenizer.getToken())) { + // token mismatch + if (runningPattern.isComplete(m_tokenizer.getToken())) + break; + if (checkOnly) + pushSyntaxToken(Mismatch); + m_errorString = "Syntax error"; + return false; + } + // token matched + if (checkOnly) + pushSyntaxToken(runningPattern.getTokenType(m_tokenizer.getToken())); + runningPattern.advance(m_tokenizer.getToken()); + m_tokenizer.nextToken(); + } + } + + // pattern ended + if (expressionExpected) { + // pattern terminated with an expression + if (position == Grammar::ExpressionEnd) { + // "E op . E" + flushPatterns(runningPattern.getPriority(), stackMinIndex, checkOnly); + m_patternStack.push_back(runningPattern); + } else { + // "op . E" + m_patternStack.push_back(runningPattern); + } + m_position = position = Grammar::ExpressionStart; + } else { + // pattern terminates with a keyword + if (position == Grammar::ExpressionEnd) { + // "E op ." + flushPatterns(runningPattern.getPriority(), stackMinIndex, checkOnly); + } else { + // "leaf .", "f(E) .", "(E) ." + } + if (!checkOnly) + runningPattern.createNode(m_calculator, m_nodeStack); + count++; + m_position = position = Grammar::ExpressionEnd; + } + } + if (count == 0 || expressionExpected) { + + m_errorString = "Expression expected"; + return false; + } + flushPatterns(-1, stackMinIndex, checkOnly); + return true; +} + +//------------------------------------------------------------------- + +Calculator *Parser::parse(string text) +{ + m_imp->m_tokenizer.setBuffer(text); + clearPointerContainer(m_imp->m_nodeStack); + m_imp->m_errorString = ""; + m_imp->m_isValid = false; + m_imp->m_calculator = new Calculator(); + bool ret = m_imp->parseExpression(false); + if (ret && !m_imp->m_nodeStack.empty()) { + m_imp->m_calculator->setRootNode(m_imp->m_nodeStack.back()); + m_imp->m_nodeStack.pop_back(); + m_imp->m_isValid = true; + } else { + delete m_imp->m_calculator; + m_imp->m_calculator = 0; + } + clearPointerContainer(m_imp->m_nodeStack); + Calculator *calculator = m_imp->m_calculator; + m_imp->m_calculator = 0; + return calculator; +} + +//------------------------------------------------------------------- + +Parser::SyntaxStatus Parser::checkSyntax(std::vector &tokens, string text) +{ + m_imp->m_tokenizer.setBuffer(text); + if (m_imp->m_tokenizer.eos()) + return Incomplete; + bool ret = m_imp->parseExpression(true); + tokens = m_imp->m_syntaxTokens; + if (ret && m_imp->m_tokenizer.eos()) + return Correct; + if (tokens.empty()) + return Incomplete; + SyntaxToken &lastToken = tokens.back(); + if (lastToken.m_type == Eos) + return Incomplete; + + int k = lastToken.m_pos + lastToken.m_length; + if (k >= (int)text.length()) { + if (lastToken.m_type < Unknown) { + lastToken.m_type = Unknown; + } + return ExtraIgnored; + } + + SyntaxToken extraIgnored; + extraIgnored.m_type = Unknown; + extraIgnored.m_pos = k; + extraIgnored.m_length = text.length() - k; + tokens.push_back(extraIgnored); + + return Error; +} + +//------------------------------------------------------------------- + +void Parser::getSuggestions(Grammar::Suggestions &suggestions, string text) +{ + std::vector tokens; + Parser::SyntaxStatus status = checkSyntax(tokens, text); + suggestions.clear(); + if (status == Correct || status == Incomplete || status == ExtraIgnored) + m_imp->m_grammar->getSuggestions(suggestions, m_imp->m_position); +} + +//------------------------------------------------------------------- + +string Parser::getCurrentPatternString(string text) +{ + return "ohime"; +} + +//------------------------------------------------------------------- + +bool Parser::isValid() const +{ + return m_imp->m_isValid; +} + +//------------------------------------------------------------------- + +string Parser::getText() const +{ + return m_imp->m_tokenizer.getBuffer(); +} + +//------------------------------------------------------------------- + +string Parser::getError() const +{ + return m_imp->m_errorString; +} + +//------------------------------------------------------------------- + +std::pair Parser::getErrorPos() const +{ + if (m_imp->m_errorString == "") + return std::make_pair(0, -1); + Token token = m_imp->m_tokenizer.getToken(); + return std::make_pair(token.getPos(), token.getPos1()); +} + +//------------------------------------------------------------------- + +} // namespace TSyntax diff --git a/toonz/sources/common/expressions/ttokenizer.cpp b/toonz/sources/common/expressions/ttokenizer.cpp new file mode 100644 index 0000000..ae21312 --- /dev/null +++ b/toonz/sources/common/expressions/ttokenizer.cpp @@ -0,0 +1,206 @@ + + +#include "ttokenizer.h" +#include + +namespace TSyntax +{ + +int Token::getIntValue() const +{ + return QString::fromStdString(getText()).toInt(); +} + +double Token::getDoubleValue() const +{ + return QString::fromStdString(getText()).toDouble(); +} + +//=================================================================== + +Tokenizer::Tokenizer() + : m_buffer(), m_index(0) +{ +} + +//------------------------------------------------------------------- + +Tokenizer::Tokenizer(string buffer) + : m_buffer(), m_index(0) +{ + setBuffer(buffer); +} + +//=================================================================== + +Tokenizer::~Tokenizer() +{ +} + +//------------------------------------------------------------------- + +void Tokenizer::setBuffer(string buffer) +{ + m_buffer = buffer + '\0'; + m_index = 0; + m_tokens.clear(); + + bool stringBlock = false; + + int i = 0; + const char *s = &m_buffer[0]; + for (;;) { + while (isascii(s[i]) && isspace(s[i])) + i++; + + int j = i; + + if (s[i] == '\0') { + m_tokens.push_back(Token("", Token::Eos, j)); + break; + } + + if (s[i] == '"') { + stringBlock = !stringBlock; + m_tokens.push_back(Token("\"", Token::Punct, j)); + + ++i; + continue; + } + + string token; + + if (stringBlock) { + // string block - read mercilessly until either another '"' or EOS + token = string(1, s[i++]); + + while (s[i] != '"' && s[i] != '\0') + token.append(1, s[i++]); + + m_tokens.push_back(Token(token, Token::Ident, j)); + } else if (isascii(s[i]) && isalpha(s[i]) || s[i] == '_') { + // ident + token = string(1, s[i++]); + + while (isascii(s[i]) && (isalpha(s[i]) || s[i] == '_' || isdigit(s[i]))) + token.append(1, s[i++]); + + m_tokens.push_back(Token(token, Token::Ident, j)); + } else if (isascii(s[i]) && isdigit(s[i]) || s[i] == '.') { + // number + while (isascii(s[i]) && isdigit(s[i])) + token.append(1, s[i++]); + + if (s[i] == '.') { + token.append(1, s[i++]); + + while (isascii(s[i]) && isdigit(s[i])) + token.append(1, s[i++]); + + if ((s[i] == 'e' || s[i] == 'E') && + (isascii(s[i + 1]) && isdigit(s[i + 1]) || + (s[i + 1] == '-' || s[i + 1] == '+') && + isascii(s[i + 2]) && isdigit(s[i + 2]))) { + token.append(1, s[i++]); + + if (s[i] == '-' || s[i] == '+') + token.append(1, s[i++]); + + while (isascii(s[i]) && isdigit(s[i])) + token.append(1, s[i++]); + } + } + m_tokens.push_back(Token(token, Token::Number, j)); + } else { + // punct. + if (s[i + 1] != '\0') { + token = string(s + i, 2); + + const string ss[] = { + "==", "!=", ">=", "<=", "||", "&&"}; + + const int m = tArrayCount(ss); + if (std::find(ss, ss + m, token) != ss + m) + i += 2; + else + token = string(1, s[i++]); + } else + token = string(1, s[i++]); + + m_tokens.push_back(Token(token, Token::Punct, j)); + } + } +} + +//------------------------------------------------------------------- + +int Tokenizer::getTokenCount() const +{ + return m_tokens.size(); +} + +//------------------------------------------------------------------- + +const Token &Tokenizer::getToken(int index) const +{ + assert(0 <= index && index < getTokenCount()); + return m_tokens[index]; +} + +//------------------------------------------------------------------- + +void Tokenizer::reset() +{ + m_index = 0; +} + +//------------------------------------------------------------------- + +const Token &Tokenizer::getToken() +{ + return getToken(m_index); +} + +//------------------------------------------------------------------- + +Token Tokenizer::nextToken() +{ + Token token = getToken(); + if (m_index + 1 < getTokenCount()) + m_index++; + return token; +} + +//------------------------------------------------------------------- + +bool Tokenizer::eos() const +{ + return m_index + 1 == getTokenCount(); +} + +//------------------------------------------------------------------- + +Token Tokenizer::getTokenFromPos(int pos) const +{ + int len = m_buffer.length(); + if (pos < 0 || pos >= len) + return Token(pos); + int x = 0; + for (int i = 0; i < getTokenCount(); i++) { + const Token &token = getToken(i); + int y = token.getPos(); + if (pos < y) { + assert(x < y); + return Token(x, y - 1); + } + x = y + (int)token.getText().length(); + if (pos < x) + return token; + } + assert(x < len); + return Token(x, len - 1); +} + +//=================================================================== + +} // TSyntax diff --git a/toonz/sources/common/flash/F3SDK.h b/toonz/sources/common/flash/F3SDK.h new file mode 100644 index 0000000..0ee0296 --- /dev/null +++ b/toonz/sources/common/flash/F3SDK.h @@ -0,0 +1,32 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: F3SDK.h + + This header-file includes all the header-files of Flash File Format SDK + low-level manager. + +****************************************************************************************/ + +#ifndef F3SDK_INCLUDED +#define F3SDK_INCLUDED + +#include "Macromedia.h" +#include "FObj.h" +#include "FAction.h" +#include "FCT.h" +#include "FDT.h" +#include "FDTBitmaps.h" +#include "FDTButtons.h" +#include "FDTFonts.h" +#include "FDTShapes.h" +#include "FDTSounds.h" +#include "FDTSprite.h" +#include "FDTText.h" +#include "FPrimitive.h" + +#endif diff --git a/toonz/sources/common/flash/FAction.cpp b/toonz/sources/common/flash/FAction.cpp new file mode 100644 index 0000000..b153a1d --- /dev/null +++ b/toonz/sources/common/flash/FAction.cpp @@ -0,0 +1,842 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FAction.cpp + + This source file contains the definition for the low-level action-related functions, + grouped by classes, which are all derived from class FAction (except FActionCondition): + + + Class Member Function + + FActionGetURL FActionGetURL(FString*, FString*); + ~FActionGetURL(); + void WriteToSWFStream(FSWFStream*); + + FActionGetURL2 FActionGetURL2(U8); + void WriteToSWFStream(FSWFStream*); + + FActionGotoFrame FActionGotoFrame(U16); + void WriteToSWFStream(FSWFStream*); + + FActionGotoFrame2 FActionGotoFrame2(U8); + void WriteToSWFStream(FSWFStream*); + + FActionGotoLabel FActionGotoLabel(FString*); + ~FActionGotoLabel(); + void WriteToSWFStream(FSWFStream*); + + FActionIf FActionIf(S16); + void WriteToSWFStream(FSWFStream*); + + FActionJump FActionJump(S16); + void WriteToSWFStream(FSWFStream*); + + FActionPush FActionPush(FString*); + FActionPush(FLOAT); + void WriteToSWFStream(FSWFStream*); + + FActionSetTarget FActionSetTarget(FString*); + ~FActionSetTarget(); + void WriteToSWFStream(FSWFStream*); + + FActionWaitForFrame FActionWaitForFrame(U16, U16); + void WriteToSWFStream(FSWFStream*); + + FActionWaitForFrame2 FActionWaitForFrame2(U8); + void WriteToSWFStream(FSWFStream*); + + FActionCondition FActionCondition(); + ~FActionCondition(); + Clear(); + AddActionRecord(FActionRecord*); + AddKeyCode(U32); + AddCondition(U16); + void WriteToSWFStream(FSWFStream*); + void WriteToSWFStream(FSWFStream*, int); + + + Functions of these classes have not been implemented yet: + + class FActionAdd; + class FActionAnd; + class FActionAsciiToChar; + class FActionCall; + class FActionCharToAscii; + class FActionCloneSprite; + class FActionDivide; + class FActionEndDrag; + class FActionEquals; + class FActionGetProperty; + class FActionGetTime; + class FActionGetVariable; + class FActionLess; + class FActionMBAsciiToChar; + class FActionMBCharToAscii; + class FActionMBStringExtract; + class FActionMBStringLength; + class FActionMultiply; + class FActionNextFrame; + class FActionNot; + class FActionOr; + class FActionPlay; + class FActionPop; + class FActionPrevFrame; + class FActionRandomNumber; + class FActionRemoveSprite; + class FActionSetProperty; + class FActionSetTarget2; + class FActionSetVariable; + class FActionStartDrag; + class FActionStop; + class FActionStopSounds; + class FActionStringAdd; + class FActionStringEquals; + class FActionStringExtract; + class FActionStringLength; + class FActionStringLess; + class FActionSubtract; + class FActionToggleQuality; + class FActionToInteger; + class FActionTrace. + + +****************************************************************************************/ + +#include "FPrimitive.h" +#include "FAction.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionUnparsed ---------------------------------------------------------- + +FActionUnparsed::FActionUnparsed(UCHAR _code, USHORT _length, UCHAR *_data) : code(_code) +{ + if (code >= 0x80) + length = _length, data = _data; + else + length = 0, data = 0; +} + +void FActionUnparsed::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteByte(code); + if (code >= 0x80) { + _SWFStream->WriteWord(length); + for (int i = 0; i < length; i++) + _SWFStream->WriteByte(data[i]); + } +} +////////////////////////////////////////////////////////////////////////////////////// + +FActionGetURL::FActionGetURL(FString *_url, FString *_window) +{ + url = _url; + window = _window; +} + +FActionGetURL::~FActionGetURL() +{ + delete url; + delete window; +} + +void FActionGetURL::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + url->WriteToSWFStream(&body, true); + + window->WriteToSWFStream(&body, true); + + _SWFStream->WriteByte(sactionGetURL); + _SWFStream->WriteWord(body.Size()); + + _SWFStream->Append(&body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionGetURL2 --------------------------------------------------------- + +FActionGetURL2::FActionGetURL2(U8 _method) +{ + method = _method; +} + +void FActionGetURL2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //sactionEquals: enumerated in "Macromedia.h" + _SWFStream->WriteByte(sactionGetURL2); + + //write the length since the high bit is set in the action tag + _SWFStream->WriteWord(1); + + //write the method + _SWFStream->WriteByte(method); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionGotoFrame ------------------------------------------------------- + +FActionGotoFrame::FActionGotoFrame(U16 _frameIndex) +{ + frameIndex = _frameIndex; +} + +void FActionGotoFrame::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //enumerated constant (see "Macromedia.h") + _SWFStream->WriteByte(sactionGotoFrame); + + _SWFStream->WriteWord(2); + + _SWFStream->WriteWord((U32)frameIndex); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionGotoFrame2 ------------------------------------------------------ + +// Constructor takes in play flag. + +FActionGotoFrame2::FActionGotoFrame2(U8 _play) +{ + + play = _play; +} + +void FActionGotoFrame2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //sactionGotoFrame2: enumerated in "Macromedia.h" + _SWFStream->WriteByte(sactionGotoFrame2); + + //write the length since the high bit is set in the action tag + _SWFStream->WriteWord(1); + + //write the play flag + _SWFStream->WriteByte(play); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionGotoLabel ------------------------------------------------------- + +FActionGotoLabel::FActionGotoLabel(FString *_label) +{ + label = _label; +} + +FActionGotoLabel::~FActionGotoLabel() +{ + + delete label; +} + +void FActionGotoLabel::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //enumerated type (see "Macromedia.h") + _SWFStream->WriteByte(sactionGotoLabel); + + label->WriteToSWFStream(_SWFStream, true); +} + +/////////////////////////////////////////////////////////////////////////////// +// -------- FActionIf ------------------------------------------------------- + +// Constructor records the branch offset. + +FActionIf::FActionIf(S16 _branchOffset) +{ + + branchOffset = _branchOffset; +} + +void FActionIf::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //enumerations in "Macromedia.h" + _SWFStream->WriteByte(sactionIf); + + //write the length since the high bit is set in the action tag + _SWFStream->WriteWord(2); + + //write the offset + _SWFStream->WriteWord((U32)branchOffset); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionJump ------------------------------------------------------- + +// Constructor records the branch offset. + +FActionJump::FActionJump(S16 _branchOffset) +{ + + branchOffset = _branchOffset; +} + +void FActionJump::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //enumerations in "Macromedia.h" + _SWFStream->WriteByte(sactionJump); + + //write the length since the high bit is set in the action tag + _SWFStream->WriteWord(2); + + //write the offset + _SWFStream->WriteWord((U32)branchOffset); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionPush ------------------------------------------------------- +// Constructor for string push. + +FActionPush::FActionPush(FString *_string) +{ + + string = _string; + number = 0; + cValue = 0; + dValue = 0; + type = 0; +} + +FActionPush::FActionPush(FLOAT _number) +{ + + number = _number; + string = 0; + cValue = 0; + dValue = 0; + type = 1; +} + +// Constructor for float push. + +FActionPush::FActionPush(U8 _type, FLOAT _number) +{ + + number = _number; + string = 0; + cValue = 0; + dValue = 0; + type = _type; +} + +FActionPush::FActionPush(double _number) +{ + number = 0; + string = 0; + cValue = 0; + dValue = _number; + type = 6; +} + +FActionPush::FActionPush(U8 _type, U8 _value) +{ + number = 0; + string = 0; + cValue = _value; + dValue = 0; + type = _type; +} + +void FActionPush::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //sactionPush: enumerated in "Macromedia.h" + _SWFStream->WriteByte(sactionPush); + + U16 size = 0; + FSWFStream temp; + // un byte c'e' sempre per memorizzare il tipo + switch (type) { + case 0: + size = 1 + string->Length() + 1; //bisogna tener conto che la deve essere null terminated + string->WriteToSWFStream(&temp, true); + break; + case 1: + case 7: + size = 5; + temp.WriteDWord((U32)number); + break; + case 4: + case 5: + case 8: + size = 2; + temp.WriteByte(cValue); + break; + case 6: { + size = 9; + U8 *bPtr = (U8 *)&dValue; +#ifndef __LP64__ + temp.WriteByte((U32)(bPtr + 4)); + temp.WriteByte((U32)(bPtr + 5)); + temp.WriteByte((U32)(bPtr + 6)); + temp.WriteByte((U32)(bPtr + 7)); + temp.WriteByte((U32)bPtr); + temp.WriteByte((U32)(bPtr + 1)); + temp.WriteByte((U32)(bPtr + 2)); + temp.WriteByte((U32)(bPtr + 3)); +#else + typedef unsigned long U64; + temp.WriteByte((U64)(bPtr + 4)); + temp.WriteByte((U64)(bPtr + 5)); + temp.WriteByte((U64)(bPtr + 6)); + temp.WriteByte((U64)(bPtr + 7)); + temp.WriteByte((U64)bPtr); + temp.WriteByte((U64)(bPtr + 1)); + temp.WriteByte((U64)(bPtr + 2)); + temp.WriteByte((U64)(bPtr + 3)); +#endif + break; + } + default: + break; + } + _SWFStream->WriteWord(size); + + //write the type + _SWFStream->WriteByte((U32)type); + + //write the object to be pushed + _SWFStream->Append(&temp); +} + +/*//originale +// Writes to the given FSWFStream. +void FActionPush::WriteToSWFStream(FSWFStream *_SWFStream){ + + //sactionPush: enumerated in "Macromedia.h" + _SWFStream->WriteByte(sactionPush); + + // un byte c'e' sempre per memorizzare il tipo + //since tag's high bit is set, must write the length + if ( type == 0 ) { + _SWFStream->WriteWord( 1 + (U32)string->Length() + 1); //bisogna tener conto che la deve essere null terminated + } else { + _SWFStream->WriteWord( 1 + 4 ); //scrive una Dword => 4 bytes + } + + //write the type + _SWFStream->WriteByte((U32)type); + + //write the object to be pushed + if ( type == 0 ) { + string->WriteToSWFStream(_SWFStream, true); + } else { + _SWFStream->WriteDWord( (U32)number ); + } +} +*/ + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionSetTarget ------------------------------------------------------- +FActionSetTarget::FActionSetTarget(FString *_targetName) +{ + targetName = _targetName; +} + +FActionSetTarget::~FActionSetTarget() +{ + delete targetName; +} + +void FActionSetTarget::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteByte(sactionSetTarget); + _SWFStream->WriteWord(targetName->Length() + 1); + targetName->WriteToSWFStream(_SWFStream, true); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionWaitForFrame ---------------------------------------------------- +FActionWaitForFrame::FActionWaitForFrame(U16 index, U16 count) +{ + // frameIndex = frameIndex; + // skipCount = skipCount; + frameIndex = index; // fixed from DV + skipCount = count; +} + +void FActionWaitForFrame::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + // enumerations in "Macromedia.h" + _SWFStream->WriteByte(sactionWaitForFrame); + + _SWFStream->WriteWord(3); + _SWFStream->WriteWord((U32)frameIndex); + _SWFStream->WriteByte((U32)skipCount); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// -------- FActionWaitForFrame2 ---------------------------------------------------- + +FActionWaitForFrame2::FActionWaitForFrame2(U8 _skipCount) +{ + + skipCount = _skipCount; +} + +void FActionWaitForFrame2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + // enumerations in "Macromedia.h" + _SWFStream->WriteByte(sactionWaitForFrame2); + + //write the length since high bit in tag id is set + _SWFStream->WriteWord(1); + + //write the skip count + _SWFStream->WriteByte(skipCount); +} + +// Constructor. Initially, there are no actions or conditions so the conditions flag +// equals zero, and the action record list is empty. + +FActionCondition::FActionCondition() +{ + + conditionFlags = 0; +} + +// Deletes all of the action records in the action record list. + +FActionCondition::~FActionCondition() +{ + + while (!actionRecordList.empty()) { + + delete actionRecordList.front(); + actionRecordList.pop_front(); + } +} + +void FActionCondition::Clear() +{ + while (!actionRecordList.empty()) { + + delete actionRecordList.front(); + actionRecordList.pop_front(); + } +} + +// Adds an action record to the action record list + +void FActionCondition::AddActionRecord(FActionRecord *_ActionRecord) +{ + + actionRecordList.push_back(_ActionRecord); +} + +// Adds the key code information to the action condition (Flash 4). + +void FActionCondition::AddKeyCode(U32 keyCode) +{ + + conditionFlags |= (keyCode << 9); +} + +// Adds a condition to the list of conditions for which the actions in the action record list +// will be taken. Sees what condition is passed, and sets the bit which corresponds to the +// condition in the conditionFlags field to 1. + +void FActionCondition::AddCondition(U16 _condition) +{ + + switch (_condition) + + { + + case OverDownToIdle: + conditionFlags = conditionFlags | 256; //this bitwise OR has the same effect as making the 8th bit a "1" + break; + case IdleToOverDown: + conditionFlags = conditionFlags | 128; //makes 9th bit a "1" ... etc. + break; + case OutDownToIdle: + conditionFlags = conditionFlags | 64; + break; + case OutDownToOverDown: + conditionFlags = conditionFlags | 32; + break; + case OverDownToOutDown: + conditionFlags = conditionFlags | 16; + break; + case OverDownToOverUp: + conditionFlags = conditionFlags | 8; + break; + case OverUpToOverDown: + conditionFlags = conditionFlags | 4; + break; + case OverUpToIdle: + conditionFlags = conditionFlags | 2; + break; + case IdleToOverUp: + conditionFlags = conditionFlags | 1; + } +} + +// Writes the action condition to the given FSWFStream. Since the action condition contains +// an offset field to the next action condition, the body of the action condition must first +// be written to a temporary stream so that size can be calculated for the offset. + +void FActionCondition::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream temp; + + temp.WriteWord((U32)conditionFlags); + int boh = temp.Size(); + + std::list::iterator cursor; + for (cursor = actionRecordList.begin(); cursor != actionRecordList.end(); cursor++) { + + (*cursor)->WriteToSWFStream(&temp); + boh = temp.Size(); + } + + //add 2 to the size because the offset is actually to the beginning of the + //next action condition's conditionFlags + _SWFStream->WriteWord(2 + temp.Size()); + + _SWFStream->Append(&temp); +} + +// Writes the action condition to the given stream. The lastFlag indicates that this is the +// last action condition so the offset is set to 0 by default. + +void FActionCondition::WriteToSWFStream(FSWFStream *_SWFStream, int /*lastFlag*/) +{ + + _SWFStream->WriteWord(0); //the offset + + _SWFStream->WriteWord((U32)conditionFlags); + + std::list::iterator cursor; + for (cursor = actionRecordList.begin(); cursor != actionRecordList.end(); cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + } + + //_SWFStream->FlushBits(); + + _SWFStream->WriteByte((U32)0); +} + +/* +FActionConditionList::FActionConditionList(){ +} + + +FActionConditionList::~FActionConditionList(){ + + while (!conditionList.empty()){ + + delete conditionList.front(); + + conditionList.pop_front(); + + } + +} + + +void FActionConditionList::AddActionCondition(FActionCondition* _ActionCondition){ + + conditionList.push_back(_ActionCondition); + +} + +U32 FActionConditionList::Size(){ + + return conditionList.size(); + + } + + + void FActionConditionList::WriteToSWFStream(FSWFStream* _SWFStream){ + + std::list::iterator cursor; + std::list::iterator cursor2; + cursor2 = (conditionList.end()); + cursor2--; + + for (cursor = conditionList.begin(); cursor != cursor2; cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + + } + + (*cursor)->WriteToSWFStream(_SWFStream, 1); //flag indicating it is the last action condition + +} +*/ + +//============================================================================= +// ALCUNE AZIONI FLASH 5 +//============================================================================= + +FActionConstantPool::FActionConstantPool(FString *string) +{ + constantList.push_back(string); +} + +FActionConstantPool::~FActionConstantPool() +{ + while (!constantList.empty()) { + delete constantList.front(); + constantList.pop_front(); + } +} + +void FActionConstantPool::addConstant(FString *string) +{ + constantList.push_back(string); +} + +void FActionConstantPool::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream temp; + std::list::iterator it = constantList.begin(); + while (it != constantList.end()) { + (*it)->WriteToSWFStream(&temp, true); + ++it; + } + + _SWFStream->WriteByte(sactionConstantPool); + _SWFStream->WriteWord(temp.FullSize() + 2); + _SWFStream->WriteWord(constantList.size()); + _SWFStream->Append(&temp); +} + +//============================================================================= +// CLIP ACTION / CLIP ACTION RECORD +//============================================================================= + +FClipAction::FClipAction() +{ + eventFlags = 0; +} + +FClipAction::~FClipAction() +{ + Clear(); +} + +void FClipAction::Clear() +{ + while (!clipActionRecordList.empty()) { + delete clipActionRecordList.front(); + clipActionRecordList.pop_front(); + } +} + +void FClipAction::AddClipActionRecord(FClipActionRecord *_clipActionRecord) +{ + clipActionRecordList.push_back(_clipActionRecord); +} + +void FClipAction::AddFlags(U32 _flags) +{ + //e' meglio definire i flags come enumerazione che sono gia' i valori da + // mettere in OR per settare l'abilitazione del particolare evento + eventFlags |= _flags; +} + +void FClipAction::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream temp; + + //Reserved + temp.WriteWord(0); + + //allEvent + temp.WriteDWord(eventFlags); + + //tutti i clip + std::list::iterator it = clipActionRecordList.begin(); + int i = 0; + while (it != clipActionRecordList.end()) { + (*it)->WriteToSWFStream(&temp); //, i);//prova zozza + ++it; + ++i; + } + + //end clipAction + temp.WriteDWord(0); + _SWFStream->Append(&temp); +} + +//============================================================================= + +FClipActionRecord::FClipActionRecord() +{ + eventFlags = 0; + keyCode = 0; +} + +FClipActionRecord::~FClipActionRecord() +{ + Clear(); +} + +void FClipActionRecord::Clear() +{ + while (!actionRecordList.empty()) { + delete actionRecordList.front(); + actionRecordList.pop_front(); + } +} + +void FClipActionRecord::AddActionRecord(FActionRecord *_actionRecord) +{ + actionRecordList.push_back(_actionRecord); +} + +void FClipActionRecord::AddKeyCode(U8 _keyCode) +{ + keyCode |= _keyCode; +} + +void FClipActionRecord::AddFlags(U32 _flags) +{ + //e' meglio definire i flags come enumerazione che sono gia' i valori da + // mettere in OR per settare l'abilitazione del particolare evento + eventFlags |= _flags; +} + +void FClipActionRecord::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream temp; + //metto tutte le azioni su per sapere la size + std::list::iterator it = actionRecordList.begin(); + while (it != actionRecordList.end()) { + (*it)->WriteToSWFStream(&temp); + ++it; + } + temp.WriteByte(sactionNone); + + //e' un offset deve andare a quello dopo + U32 size = temp.FullSize(); + + FSWFStream temp1; + temp1.WriteDWord(eventFlags); + + if ((eventFlags & ClipEventKeyPress) == ClipEventKeyPress) { + temp1.WriteDWord(size + 1); + temp1.WriteByte(keyCode); + } else + temp1.WriteDWord(size); + + temp1.Append(&temp); + + _SWFStream->Append(&temp1); +} diff --git a/toonz/sources/common/flash/FAction.h b/toonz/sources/common/flash/FAction.h new file mode 100644 index 0000000..bc4c2a6 --- /dev/null +++ b/toonz/sources/common/flash/FAction.h @@ -0,0 +1,1346 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FAction.h + + This header-file contains the declarations of the low-level action-related classes: + + 1) class FActionRecord; + 2) all the derived classes of class FAction: + + class FActionAdd; + class FActionAnd; + class FActionAsciiToChar; + class FActionCall; + class FActionCharToAscii; + class FActionCloneSprite; + class FActionDivide; + class FActionEndDrag; + class FActionEquals; + class FActionGetProperty; + class FActionGetTime; + class FActionGetURL; + class FActionGetURL2; + class FActionGetVariable; + class FActionGotoFrame; + class FActionGotoFrame2; + class FActionGotoLabel; + class FActionIf; + class FActionJump; + class FActionLess; + class FActionMBAsciiToChar; + class FActionMBCharToAscii; + class FActionMBStringExtract; + class FActionMBStringLength; + class FActionMultiply; + class FActionNextFrame; + class FActionNot; + class FActionOr; + class FActionPlay; + class FActionPop; + class FActionPrevFrame; + class FActionPush; + class FActionRandomNumber; + class FActionRemoveSprite; + class FActionSetProperty; + class FActionSetTarget; + class FActionSetTarget2; + class FActionSetVariable; + class FActionStartDrag; + class FActionStop; + class FActionStopSounds; + class FActionStringAdd; + class FActionStringEquals; + class FActionStringExtract; + class FActionStringLength; + class FActionStringLess; + class FActionSubtract; + class FActionToggleQuality; + class FActionToInteger; + class FActionTrace; + class FActionWaitForFrame; + class FActionWaitForFrame2; + and, + + 3) class FActionCondition. + +****************************************************************************************/ + +#ifndef _F_ACTION_H_ +#define _F_ACTION_H_ + +#include "Macromedia.h" +#include "FSWFStream.h" + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif +class FString; + +//! A general class specifying an action to be performed by the Flash player +/*! +Buttons and DoAction tags utilize FActionRecords. +When encountered in a button tag, the Flash player adds action to a list to be processed when a button state has changed. +When encountered in a DoAction tag, the Flash player adds the action to a list to be processed when a FCTShowframe tag is encountered. +FActionRecords specifiy actions such as: starting and stoping movie play, toggling display quality, performing stack arithmetic... etc. +Many actions involve the use of a last-on/first-off stack machine (Flash 4.0) that stores string values. +\sa FCTDoAction, FDTDefineButton, FDTDefineButton2 +*/ + +class DVAPI FActionRecord +{ +public: + virtual ~FActionRecord() {} + + //! A general function that will write its object out to a FSWFStream + /*! + All FActionRecords contain the WriteToSWFStream member function, a function that will writes out its Object's data out to a FSWFStream + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; +}; + +class DVAPI FActionUnparsed : public FActionRecord +{ + UCHAR code; + USHORT length; + UCHAR *data; + +public: + FActionUnparsed(UCHAR _code, USHORT _length, UCHAR *_data); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); +}; + +//! An action that adds two numbers +/*! \verbatim +1. pops value A off the stack machine +2. pops value B off the stack machine +3. A and B are converted to floating-point (if non-numeric,converts to 0) +4. A and B are added +5. Result, A + B, is pushed back onto stack +\endverbatim */ + +class FActionAdd : public FActionRecord +{ +public: + FActionAdd() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionAdd); } +}; + +//! An action that performs a logical AND of two numbers +/*! \verbatim +1. pops value A off the stack +2. pops value B off the stack +3. A nd B are converted to floating-point(if non-numeric, converts to 0) +4. If both A and B are non-zero, a 1 is pushed onto the stack; otherwise a 0 is pushed +\endverbatim */ + +class FActionAnd : public FActionRecord +{ +public: + FActionAnd() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionAnd); } +}; + +//! An action that converts ASCII to character code +/*! \verbatim +1. pops value A off the stack +2. the value is converted from a number to the corresponding ASCII character +3. the resulting character is pushed to the stack +\endverbatim */ + +class FActionAsciiToChar : public FActionRecord +{ +public: + FActionAsciiToChar() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionAsciiToChar); } +}; + +//! An action that calls a subroutine +/*! \verbatim +1. pops value A off the stack +2. this value should be either a string matching a frame label, or a number indicating a frame number +3. The value may be prefixed by a target string identifying the movie clip that contains the frame being called +4. If the frame is successfully located, the actions in the target frame are executed. After the actions in the target frame are executed, execution resumes at the instruction after the ActionCall instruction. +5. If the frame cannot be found, nothing happens +6. NOTE: This action's tag (0x9e) has the high bit set, this is a bug +\endverbatim */ +class FActionCall : public FActionRecord +{ +public: + FActionCall() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) + { + _SWFStream->WriteByte(sactionCall); + // Write the length since the high bit is set in the action tag + _SWFStream->WriteWord(0); + } +}; + +//! An action that converts character code to ASCII +/*! \verbatim +1. pops value A off the stack +2. The first character of value a is converted to a numeric ASCII character code +3. The resulting character code is pushed to the stack +\endverbatim */ +class FActionCharToAscii : public FActionRecord +{ +public: + FActionCharToAscii() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionCharToAscii); } +}; + +//! An action that clones a sprite +/*! \verbatim +1. pops value DEPTH (new sprite depth) off the stack +2. pops value TARGET (name of new sprite) off the stack +3. pops value SOURCE (souce sprite) off stack +4. duplicate the movie SOURCE, giving the new movie the name TARGET at z-order depth DEPTH +\endverbatim */ +class FActionCloneSprite : public FActionRecord +{ +public: + FActionCloneSprite() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionCloneSprite); } +}; + +//! An action that divides two numbers +/*! \verbatim +1. pops value A off the stack +2. pops value B off stack +3. A and B are converted to floating-point (if non-numeric convert to 0) +4. Result B divided by A is pushed to stack +5. If A is 0, the result is the string "#ERROR#" +\endverbatim */ +class FActionDivide : public FActionRecord +{ +public: + FActionDivide() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionDivide); } +}; + +//! An action that ends drag operation +/*! \verbatim +1. ends the drag operation in progress (if any) +\endverbatim */ +class FActionEndDrag : public FActionRecord +{ +public: + FActionEndDrag() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionEndDrag); } +}; + +//! An action that tests two numbers for equality +/*! \verbatim +1. pops value A off the stack +2. pops value B off stack +3. A and B converted to floating-point (if non-numeric, converted to 0) +4. A and B compared for equality +5. If equal, a 1 is pushed to stack +6. Otherwise, 0 is pushed +\endverbatim */ +class FActionEquals : public FActionRecord +{ +public: + FActionEquals() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionEquals); } +}; + +//! An action that gets a movie property +/*! \verbatim +1. pops value INDEX off the stack +2. pops TARGET off the stack +3. retrieve the value of the property enumerated as INDEX from the movie clip with target path TARGET and push this value onto stack +\endverbatim */ +class FActionGetProperty : public FActionRecord +{ + +public: + FActionGetProperty() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionGetProperty); } +}; + +//! An action that reports milliseconds since player started +/*! \verbatim +1. an integer representing the number of minutes since the player was started is pushed onto stack +\endverbatim */ +class FActionGetTime : public FActionRecord +{ +public: + FActionGetTime() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionGetTime); } +}; + +//! An action that opens the given URL in a given window +/*! \verbatim +An action record specifying that the player open the given URL in a given window +\endverbatim */ +class FActionGetURL : public FActionRecord +{ +public: + //! FActionGetURL constructor + /*! + \param _url a pointer to an FString representing a URL address + \param _window a pointer to an FString identifying a window to open the URL in (empty FString indicates current window) + */ + FActionGetURL(FString *_url, FString *_window); + + virtual ~FActionGetURL(); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FString *url; + FString *window; +}; + +//! An action that opens a URL in an indicated window (stack based) +/*! \verbatim +1. pops value WINDOW off stack, window specifies the target window, empty string indicates current +2. pops URL off stack, specifies which URL to retrieve +3. retrieve URL in WINDOW using given HTTP request method + - if method is "HTTP GET" or "HTTP POST", variables in movie clip are sent using standard "x-www-urlencoded" encoding +\endverbatim */ +class FActionGetURL2 : public FActionRecord +{ +public: + //! FActionGetURL2 constructor + /*! + \param _method a number indicating an HTTP request method (value 0 indicates that it is not a request method, 1 indicates HTTP GET, 2 indicates HTTP POST) + */ + FActionGetURL2(U8 _method); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 method; +}; + +//! An action that gets a variable's value +/*! \verbatim +1. pops value NAME off the stack +2. retrieves value of variable NAME +3. pushes NAME's associated value back onto stack +Variables in other execution contexts may be referenced by prefixing a variable name with a target path followed by a colon +\endverbatim */ +class FActionGetVariable : public FActionRecord +{ +public: + FActionGetVariable() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionGetVariable); } +}; + +//! An action that goes to the specified frame +/*! \verbatim +An action record specifying that the player goto the frame specified by the given frame index +\endverbatim */ +class DVAPI FActionGotoFrame : public FActionRecord +{ +public: + //! FActionGotoFrame constructor + /*! + \param frameIndex a number specifying the frame index of a frame to go to + */ + FActionGotoFrame(U16 frameIndex); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 frameIndex; +}; + +//! An action that goes to a identified frame (stack based) +/*! \verbatim +1. pops value FRAME off the stack +2. if frame is a number, the next movie frame displayed will be FRAME'th frame in the current movie +3. if frame is a string, the next movie frame displayed will be the frame designated by label "FRAME" (if no such label exists, action is ignored) +4. FRAME may be prefixed by a target path +5. if given "play" flag is set, the movie begins playing the goto frame, otherwise movie play is stopped on goto frame +\endverbatim */ +class FActionGotoFrame2 : public FActionRecord +{ +public: + //! FActionGotoFrame2 constructor + /*! + \param _play a true or false value indicating whether or not goto frame should be played + */ + + FActionGotoFrame2(U8 _play); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 play; +}; + +//! An action that goes to the frame indicated by the given label +/*! \verbatim +An action record specifying that the player goto the frame with the given label +\endverbatim */ +class FActionGotoLabel : public FActionRecord +{ +public: + //! FActionLabel constructor + /*! + \param _label a FString pointer indicating the label of the frame to go to + */ + FActionGotoLabel(FString *_label); + + virtual ~FActionGotoLabel(); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FString *label; +}; + +//! An action that test a condition and branches +/*! \verbatim +1. pops value CONDITION off the stack +2. if CONDITION is non-zero, branch to a given number of offset bytes following FActionIf record (a 0 value points to action directly following FActionIf; offset is a signed integer quantity enabling forward and backward branching) +\endverbatim */ +class DVAPI FActionIf : public FActionRecord +{ +public: + //!FActionIf constructor + /*! + \param _branchOffset a value representing a number of bytes (pos or neg) following the current tag to brach off to + */ + FActionIf(S16 _branchOffset); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + S16 branchOffset; +}; + +//! An action that performs an unconditional branch +/*! \verbatim +An action record specifying that the player branch to a given number of bytes following the FActionJump record(a 0 value points to the action record immediately following) +Can branch either forward or backward +\endverbatim */ +class FActionJump : public FActionRecord +{ +public: + //! FActionJump constructor + /*! + \param _branchOffset a value representing a number of bytes (pos or neg) following the current tagto branch off to + */ + FActionJump(S16 _branchOffset); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + S16 branchOffset; +}; + +//! An action that tests if a number is less than another number +/*! \verbatim +1. pops value A off the stack +2. pops value B off the stack +3. A and B converted to floating-point (non-numeric converted to 0) +4. if B < A, a 1 is pushed onto stack, otherwise a 0 is pushed +\endverbatim */ +class FActionLess : public FActionRecord +{ +public: + FActionLess() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionLess); } +}; + +//! An action that converts ASCII to character code (multi-byte aware) +/*! \verbatim +1. pops value VALUE off the stack +2. the value is converted from a number to the corresponding character. If 16bit value, double byte character constructed +3. resulting character pushed back onto stack +\endverbatim */ +class FActionMBAsciiToChar : public FActionRecord +{ +public: + FActionMBAsciiToChar() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionMBAsciiToChar); } +}; + +//! An action that converts character code to ASCII (multi-byte aware) +/*! \verbatim +1. pops value VALUE off the stack +2. the first character of VALUE is converted to a numeric character code. If the first character of VALUE is a double-byte character, a 16bit code value is constructed +3. resulting code is pushed to stack +\endverbatim */ +class FActionMBCharToAscii : public FActionRecord +{ +public: + FActionMBCharToAscii() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionMBCharToAscii); } +}; + +//! An action that extracts a substring from a string (multi-byte aware) +/*! \verbatim +1. pops number COUNT off the stack +2. pops number INDEX off stack +3. pops string STRING off stack +4. push to stack, the string of length LENGTH starting at the INDEX'th character of STRING +5. if INDEX or COUNT are non-numeric, push empty string to stack +\endverbatim */ +class FActionMBStringExtract : public FActionRecord +{ +public: + FActionMBStringExtract() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionMBStringExtract); } +}; + +//! An action that computes the length of a string (multi-byte aware) +/*! \verbatim +1. pops value STRING off stack +2. push length of STRING onto stack +\endverbatim */ +class FActionMBStringLength : public FActionRecord +{ +public: + FActionMBStringLength() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionMBStringLength); } +}; + +//! An action that multiplies two numbers +/*! \verbatim +1. pops value A off stack +2. pops value B off stack +3. A and B are converted to floating-point (non-numeric converted to 0) +4. result of A multiply B is pushed to stack +\endverbatim */ +class FActionMultiply : public FActionRecord +{ +public: + FActionMultiply() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionMultiply); } +}; + +//! An action that goes to next frame +/*! \verbatim +An action specifying that the Flash player goto the next frame +\endverbatim */ +class FActionNextFrame : public FActionRecord +{ +public: + FActionNextFrame() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionNextFrame); } +}; + +//! An action that performs the logical NOT of a number +/*! \verbatim +An action specifying that the Flash player: +1. pops value A off stack +2. A is converted to floating-point (non-numeric is converted to zero) +3. if A is zero, 1 pushed to stack +4. otherwise 0 pushed to stack +\endverbatim */ +class FActionNot : public FActionRecord +{ +public: + FActionNot() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionNot); } +}; + +//! An action that performs the logical OR of two numbers +/*! \verbatim +1. pops value A off stack +2. pops value B off stack +3. A and B converted to floating-point (non-numeric converted to zero) +4. if either A or B are non-zero, 1 is pushed to stack, otherwise 0 pushed to stack +\endverbatim */ +class FActionOr : public FActionRecord +{ +public: + FActionOr() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionOr); } +}; + +//! An action that starts playing the movie at the current frame +/*! \verbatim +An action specifying that the Flash player to start playing the movie at the current frame +\endverbatim */ +class FActionPlay : public FActionRecord +{ +public: + FActionPlay() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionPlay); } +}; + +//! An action that pops a value off the stack +/*! \verbatim +1. pops value A off stack and discards the value +\endverbatim */ +class FActionPop : public FActionRecord +{ +public: + FActionPop() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionPop); } +}; + +//! An action that goes to the previous frame +/*! \verbatim +\endverbatim */ +class FActionPrevFrame : public FActionRecord +{ +public: + FActionPrevFrame() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionPrevFrame); } +}; + +//! An action that pushes a given value onto the stack +/*! \verbatim +Pushes either a string or floating-point number onto the stack +\endverbatim */ +class DVAPI FActionPush : public FActionRecord +{ +public: + //! FActionPush constructor for pushing strings + /*! + \param _string a pointer to the FString that will be pushed onto stack + */ + FActionPush(FString *_string); + + //! FActionPush constructor for pushing numbers + /*! + \param _number a pointer to the FLOAT that will be pushed onto stack + */ + FActionPush(FLOAT _number); + + FActionPush(U8 _type, FLOAT _number); + FActionPush(double _number); + FActionPush(U8 _type, U8 _value); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FString *string; + FLOAT number; + U8 cValue; + double dValue; + U8 type; +}; + +//! An action that constructs a random number +/*! \verbatim +1. pop MAXIMUM off stack +2. constructs a random integer in the range 0... (MAXIMUM -1) +3. pushes this random value to stack +\endverbatim */ +class FActionRandomNumber : public FActionRecord +{ +public: + FActionRandomNumber() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionRandomNumber); } +}; + +//! An action that removes a cloned sprite +/*! \verbatim +1. pops value TARGET off stack +2. Removes the cloned clip identifiesd by target TARGET +\endverbatim */ +class FActionRemoveSprite : public FActionRecord +{ +public: + FActionRemoveSprite() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionRemoveSprite); } +}; + +//! An action that sets a movie property +/*! \verbatim +1. pops value VALUE off stack +2. pops value INDEX off stack +3. pops value TARGET off stack +4. sets property enumerated as INDEX in the movie clip TARGET to the value VALUE +\endverbatim */ +class FActionSetProperty : public FActionRecord +{ +public: + FActionSetProperty() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionSetProperty); } +}; + +//! An action that sets the context of action +/*! \verbatim +Sets context of action to target named by given string +\endverbatim */ +class FActionSetTarget : public FActionRecord +{ +public: + //! FActionSetTarget constructor + /*! + \param _targetName a pointer to the FString naming the target to set action context to + */ + FActionSetTarget(FString *_targetName); + + virtual ~FActionSetTarget(); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FString *targetName; +}; + +//! An action that sets the context of action (stack based) +/*! \verbatim +1. pops value TARGET off stack +2. sets current context of action to object identified by TARGET +\endverbatim */ +class FActionSetTarget2 : public FActionRecord +{ +public: + FActionSetTarget2() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionSetTarget2); } +}; + +//! An action that sets a variable +/*! \verbatim +1. pops value VALUE off stack +2. pops string NAME off stack +3. sets NAME to VALUE in the current execution context +\endverbatim */ +class FActionSetVariable : public FActionRecord +{ +public: + FActionSetVariable() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionSetVariable); } +}; + +//! An action that starts dragging a movie clip +/*! \verbatim +1. pops value TARGET off stack +2. pops LOCKCENTER off stack +3. pops CONSTRAIN +4. if CONSTRAIN is non-zero: + - pops y2 + - pops x2 + - pops y1 + - pops x1 +5. starts dragging of movie clip identified by TARGET +6. if LOCKCENTER is non-zero, the center of clip is locked to the mouse position, otherwise clip moves relative to starting mouse position +7. if CONSTRAIN, draged clip is constrained coordinates x1, y1, x2, y2 +\endverbatim */ +class FActionStartDrag : public FActionRecord +{ +public: + FActionStartDrag() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStartDrag); } +}; + +//! An action that stops movie play at the current frame +/*! \verbatim +\endverbatim */ +class FActionStop : public FActionRecord +{ +public: + FActionStop() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStop); } +}; + +//! An action that stops playing all sounds in movie +/*! \verbatim +\endverbatim */ +class FActionStopSounds : public FActionRecord +{ +public: + FActionStopSounds() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStopSounds); } +}; + +//! An action that concatenates two strings +/*! \verbatim +1. pops string A off stack +2. pops string B off stack +3. the concatenation of BA is pushed to stack +\endverbatim */ +class FActionStringAdd : public FActionRecord +{ +public: + FActionStringAdd() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStringAdd); } +}; + +//! An action that tests two strings for equality +/*! \verbatim +1. pops string A off stack +2. pops string B off stack +3. A and B are compared (case-sensitive) +4. if strings are equal, a 1 is pushed to stack, otherwise 0 pushed +\endverbatim */ +class FActionStringEquals : public FActionRecord +{ +public: + FActionStringEquals() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStringEquals); } +}; + +//! An action that extracts a substring from a string +/*! \verbatim +1. pops number COUNT off stack +2. pops number INDEX off stack +3. pops string STRING off stack +4. the substring of length COUNT starting at the INDEX'th index of string STRING is pushed to stack +5. if COUNT or INDEX non-numeric, push empty string +\endverbatim */ +class FActionStringExtract : public FActionRecord +{ +public: + FActionStringExtract() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStringExtract); } +}; + +//! An action that computes the length of a string +/*! \verbatim +1. pops string STRING off stack +2. the length of string STRING is pushed to stack +\endverbatim */ +class FActionStringLength : public FActionRecord +{ +public: + FActionStringLength() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStringLength); } +}; + +//! An action that test if a string is less than another string +/*! \verbatim +1. pops string A off stack +2. pops string B off stack +3. if B < A using a byte to byte comparison, a 1 is pushed to the stack, otherwise 0 is pushed to stack +\endverbatim */ +class FActionStringLess : public FActionRecord +{ +public: + FActionStringLess() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionStringLess); } +}; + +//! An action that subtracts a number from another number +/*! \verbatim +1. pops value A off stack +2. pops value B off stack +3. converts values A and B to floating-point (non-numeric converts to zero) +4. the result, B-A, is pushed to stack +\endverbatim */ +class FActionSubtract : public FActionRecord +{ +public: + FActionSubtract() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionSubtract); } +}; + +//! An action that toggles screen quality between high and low +/*! \verbatim +\endverbatim */ +class FActionToggleQuality : public FActionRecord +{ +public: + FActionToggleQuality() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionToggleQuality); } +}; + +//! An action that converts a value to an integer +/*! \verbatim +1. pops value A off stack +2. A is converted to a floating-point number +3. A is truncated to an integer value +4. truncated A is pushed to stack +\endverbatim */ +class FActionToInteger : public FActionRecord +{ +public: + FActionToInteger() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionToInteger); } +}; + +//! An action that sends debugging output string +/*! \verbatim +1. pops value VALUE off stack +2. in the "Test Movie" mode of the Flash editor, appends VALUE to the output window if the debugging level is set to "None" +3. ignored by Flash Player +\endverbatim */ +class FActionTrace : public FActionRecord +{ +public: + FActionTrace(); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionTrace); } +}; + +//! An action that waits for a specified frame, otherwise skips a specified number of actions +/*! \verbatim +\endverbatim */ +class FActionWaitForFrame : public FActionRecord +{ +public: + //! FActionWaitForFrame constructor + /*! + \param _frameIndex a number indexing the frame to wait for + \param skipCount a number indicating the number of frames to skip + */ + FActionWaitForFrame(U16 _frameIndex, U16 skipCount); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 frameIndex; + U16 skipCount; +}; + +//! An action that waits for a frame to be loaded +/*! \verbatim +1. pops value FRAME off stack +2. frame is evaluated in the same manner as in FActionGotoFrame2 +3. if the frame identified by FRAME has been loaded, skip a specified number of actions following the current one +\endverbatim */ +class FActionWaitForFrame2 : public FActionRecord +{ +public: + //! FActionWaitForFrame2 constructor + /*! + \param _skipCount a number of actions to skip + */ + FActionWaitForFrame2(U8 _skipCount); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 skipCount; +}; + +//! A class specifying a set of conditions and a set of subsequent actions to perform +/*! \verbatim +Buttons contain a set of FActionConditions. +An FActionCondition conatins a set of conditions and a set of actions to perform when the set of conditions is met + \sa FDTDefineButton, FDTDefineButton2 +*/ +class DVAPI FActionCondition +{ +public: + FActionCondition(); + + ~FActionCondition(); + + void Clear(); + + //! A member function that adds another action to the ActionCondition + /*! + Appends an FActionRecord to the list of FActionRecords. + These actions will be performed when the set of conditions is met + \param _ActionRecord any FActionRecord pointer + */ + void AddActionRecord(FActionRecord *_ActionRecord); + + //! A member function that adds a key-stroke condition to the set of conditions in an ActionCondition + /*! + \param keyCode one of the enumerated key-codes found in "Macromedia.h" + */ + void AddKeyCode(U32 keyCode); + + //! A member function that adds a button condition to the set of conditions in an ActionCondition + /*! + \param _condition one of the enumerated action-conditions found in "Macromedia.h" + */ + void AddCondition(U16 _condition); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + \sa WriteToSWFStream(FSWFStream* _SWFStream, int lastFlag), FSWFStream + */ + void WriteToSWFStream(FSWFStream *_SWFStream); + + //! Writes the object out to a FSWFStream and writes the terminating record to the FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + \param lastFlag a flag that, if true, indicates that this ActionCondition is the last record to be writen + \sa WriteToSWFStream(FSWFStream* _SWFStream), FSWFStream + */ + void WriteToSWFStream(FSWFStream *_SWFStream, int lastFlag); + +private: + std::list actionRecordList; + U16 conditionFlags; +}; + +/* +class FActionConditionList +{ +public: + FActionConditionList(); + ~FActionConditionList(); + + void AddActionCondition(FActionCondition* _ActionCondition); + U32 Size(); + void WriteToSWFStream(FSWFStream* _SWFStream); + +private: + std::list conditionList; +}; +*/ +//============================================================================= +// ALCUNE AZIONI FLASH 5 +//============================================================================= + +class FActionConstantPool : public FActionRecord +{ +public: + FActionConstantPool(FString *string); + ~FActionConstantPool(); + + void addConstant(FString *string); + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + std::list constantList; + FActionConstantPool(); +}; + +class FActionLess2 : public FActionRecord +{ +public: + FActionLess2() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionLess2); } +}; + +class FActionEquals2 : public FActionRecord +{ +public: + FActionEquals2() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionEquals2); } +}; + +class FActionCallMethod : public FActionRecord +{ +public: + FActionCallMethod() {} + + //! Writes the object out to a FSWFStream + /*! + \param _SWFStream any FSWFStream pointer, though usually the FSWFStream given as an argument to the appendTag function of the FSWFStream representing the .swf file being created + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) { _SWFStream->WriteByte(sactionCallMethod); } +}; + +//============================================================================= +// CLIP ACTION / CLIP ACTION RECORD +//============================================================================= + +class FClipActionRecord; + +class DVAPI FClipAction +{ +public: + FClipAction(); + ~FClipAction(); + + void Clear(); + void AddClipActionRecord(FClipActionRecord *_clipActionRecord); + void AddFlags(U32 _flags); + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + std::list clipActionRecordList; + U32 eventFlags; +}; + +//============================================================================= + +class DVAPI FClipActionRecord +{ +public: + FClipActionRecord(); + ~FClipActionRecord(); + + void Clear(); + void AddActionRecord(FActionRecord *_actionRecord); + void AddKeyCode(U8 _keyCode); + void AddFlags(U32 _flags); + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + std::list actionRecordList; + U32 eventFlags; + U8 keyCode; +}; +#endif diff --git a/toonz/sources/common/flash/FCT.cpp b/toonz/sources/common/flash/FCT.cpp new file mode 100644 index 0000000..db1546c --- /dev/null +++ b/toonz/sources/common/flash/FCT.cpp @@ -0,0 +1,502 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FCT.cpp + + This source file contains the definition for the low-level Control-Tag related functions, + grouped by classes, which are all derived from class FCT: + + + Class Member Function + + FCTDoAction FCTDoAction(void); + ~FCTDoAction(); + void AddAction(FActionRecord*); + void WriteToSWFStream(FSWFStream*); + + FCTFrameLabel FCTFrameLabel(FString*); + ~FCTFrameLabel(); + void WriteToSWFStream(FSWFStream*); + + FCTPlaceObject FCTPlaceObject(U16, U16, FMatrix*, FCXForm*); + ~FCTPlaceObject(); + void WriteToSWFStream(FSWFStream*); + + FCTPlaceObject2 FCTPlaceObject2(U16, U16, U16, U16, U16, U16, FMatrix*, + FCXForm*, U16, FString*, U16); + ~FCTPlaceObject2(); + void WriteToSWFStream(FSWFStream*); + + FCTProtect FCTProtect(); + void WriteToSWFStream(FSWFStream*); + + FCTRemoveObject FCTRemoveObject(U16, U16); + void WriteToSWFStream(FSWFStream*); + + FCTRemoveObject2 FCTRemoveObject2(U16); + void WriteToSWFStream(FSWFStream*); + + FCTSetBackgroundColor FCTSetBackgroundColor(FColor*); + ~FCTSetBackgroundColor(); + void WriteToSWFStream(FSWFStream*); + + FCTShowFrame FCTShowFrame(); + U32 IsShowFrame(); + void WriteToSWFStream(FSWFStream*); + + FCTStartSound FCTStartSound(U16, FSoundInfo*); + ~FCTStartSound(); + void WriteToSWFStream(FSWFStream*); + + +****************************************************************************************/ + +#include "FCT.h" +#include "FDTShapes.h" +#include "FDTSounds.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCTDoAction ------------------------------------------------------------ + +FCTDoAction::FCTDoAction(void) +{ +} + +FCTDoAction::~FCTDoAction() +{ + + while (!actionRecordList.empty()) { + + delete actionRecordList.front(); + + actionRecordList.pop_front(); + } +} + +void FCTDoAction::AddAction(FActionRecord *_actionRecord) +{ + + actionRecordList.push_back(_actionRecord); +} + +void FCTDoAction::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + std::list::iterator cursor; + for (cursor = actionRecordList.begin(); cursor != actionRecordList.end(); cursor++) { + + (*cursor)->WriteToSWFStream(&body); + } + + body.WriteByte(0); + + _SWFStream->AppendTag(stagDoAction, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCTFrameLabel ---------------------------------------------------------- + +FCTFrameLabel::FCTFrameLabel(FString *_frameName) +{ + frameName = _frameName; +} + +FCTFrameLabel::~FCTFrameLabel() +{ + delete frameName; +} + +void FCTFrameLabel::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + frameName->WriteToSWFStream(&body, true); + + _SWFStream->AppendTag(stagFrameLabel, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCTPlaceObject ---------------------------------------------------------- + +FCTPlaceObject::FCTPlaceObject(U16 _characterID, + U16 _depth, + FMatrix *_matrix, + FCXForm *_colorTransform) +{ + characterID = _characterID; + depth = _depth; + matrix = _matrix; + colorTransform = _colorTransform; + + FLASHASSERT(_colorTransform); +} + +FCTPlaceObject::~FCTPlaceObject() +{ + delete matrix; + delete colorTransform; +} +void FCTPlaceObject::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + ; + + body.WriteWord((U32)characterID); + + body.WriteWord((U32)depth); + + matrix->WriteToSWFStream(&body); + + if (colorTransform) { + colorTransform->WriteToSWFStream(&body); + } + + _SWFStream->AppendTag(stagPlaceObject, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCTPlaceObject2 -------------------------------------------------------- + +FCTPlaceObject2::FCTPlaceObject2(U16 _hasClipDepth, + U16 _hasRatio, + U16 _hasChar, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, + FCXFormWAlpha *_colorTransform, + U16 _ratio, + FString *_name, + U16 _clipDepth) +{ + SetParameters(_hasClipDepth, _hasRatio, _hasChar, _hasMove, _depth, _characterID, + _matrix, _colorTransform, _ratio, _name, _clipDepth); +} + +FCTPlaceObject2::FCTPlaceObject2(const FCTPlaceObject2 &obj) +{ + SetParameters(obj.hasClipDepth, obj.hasRatio, obj.hasCharID, obj.hasMove, + obj.depth, obj.characterID, 0, obj.colorTransform, + obj.ratio, 0, obj.clipDepth); + matrix = obj.matrix ? new FMatrix(*(obj.matrix)) : 0; + name = obj.name ? new FString(*(obj.name)) : 0; +} + +void FCTPlaceObject2::SetParameters(U16 _hasClipDepth, + U16 _hasRatio, + U16 _hasChar, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, + FCXFormWAlpha *_colorTransform, + U16 _ratio, + FString *_name, + U16 _clipDepth) +{ + hasClipAction = 0; + hasClipDepth = _hasClipDepth; + hasRatio = _hasRatio; + hasCharID = _hasChar; + hasMove = _hasMove; + depth = _depth; + characterID = _characterID; + matrix = _matrix; + colorTransform = _colorTransform; + ratio = _ratio; + name = _name; + clipDepth = _clipDepth; + clipAction = NULL; +} + +FCTPlaceObject2::FCTPlaceObject2(U16 _hasClipAction, + U16 _hasClipDepth, + U16 _hasRatio, + U16 _hasChar, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, + FCXFormWAlpha *_colorTransform, + U16 _ratio, + FString *_name, + U16 _clipDepth, + FClipAction *_clipAction) +{ + hasClipAction = _hasClipAction; + hasClipDepth = _hasClipDepth; + hasRatio = _hasRatio; + hasCharID = _hasChar; + hasMove = _hasMove; + depth = _depth; + characterID = _characterID; + matrix = _matrix; + colorTransform = _colorTransform; + ratio = _ratio; + name = _name; + clipDepth = _clipDepth; + clipAction = _clipAction; +} + +FCTPlaceObject2::~FCTPlaceObject2() +{ + + delete name; + delete matrix; + delete colorTransform; + delete clipAction; +} + +U32 FCTPlaceObject2::IsPlaceObject() +{ + + return 1; +} + +int FCTPlaceObject2::GetPlacedId() +{ + return hasCharID ? characterID : -1; +} + +void FCTPlaceObject2::SetId(U16 id) +{ + characterID = id; + hasMove = 0; + hasCharID = 1; +} + +void FCTPlaceObject2::ChangePlacedId(U16 id) +{ + if (hasCharID) { + characterID = id; + //hasMove=0; + } +} + +void FCTPlaceObject2::SetMatrix(FMatrix *_matrix) +{ + matrix = _matrix; +} + +void FCTPlaceObject2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteBits(hasClipAction, 1); /*DM*/ + body.WriteBits(hasClipDepth, 1); /*DM*/ + body.WriteBits((name != 0), 1); + body.WriteBits(hasRatio, 1); + body.WriteBits((colorTransform != 0), 1); + body.WriteBits((matrix != 0), 1); + body.WriteBits(hasCharID, 1); + body.WriteBits(hasMove, 1); + + body.WriteWord(depth); + + if (hasCharID) + body.WriteWord((U32)characterID); + if (matrix) + matrix->WriteToSWFStream(&body); + if (colorTransform) + colorTransform->WriteToSWFStream(&body); + if (hasRatio) + body.WriteWord((U32)ratio); + if (name) + name->WriteToSWFStream(&body, true); + if (hasClipDepth) /*DM*/ + body.WriteWord((U32)clipDepth); /*DM*/ + if (hasClipAction) + clipAction->WriteToSWFStream(&body); + + body.FlushBits(); + + _SWFStream->AppendTag(stagPlaceObject2, body.Size(), &body); +} + +///////////////////////////////////////////////////////////////////////////////// +// -------- FCTProtect -------------------------------------------------------- + +FCTProtect::FCTProtect() +{ +} + +void FCTProtect::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->AppendTag(stagProtect, 0, 0); +} + +///////////////////////////////////////////////////////////////////////////////// +// -------- FCTRemoveObject --------------------------------------------------- + +FCTRemoveObject::FCTRemoveObject(U16 _characterID, U16 _depth) +{ + characterID = _characterID; + depth = _depth; +} + +void FCTRemoveObject::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + body.WriteWord((U32)depth); + + _SWFStream->AppendTag(stagRemoveObject, body.Size(), &body); +} + +///////////////////////////////////////////////////////////////////////////////// +// -------- FCTRemoveObject2 -------------------------------------------------- + +FCTRemoveObject2::FCTRemoveObject2(U16 _depth) +{ + depth = _depth; +} + +void FCTRemoveObject2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + body.WriteWord((U32)depth); + + _SWFStream->AppendTag(stagRemoveObject2, body.Size(), &body); +} + +///////////////////////////////////////////////////////////////////////////////// +// -------- FCTSetBackgroundColor -------------------------------------------------- + +FCTSetBackgroundColor::FCTSetBackgroundColor(FColor *_color) +{ + color = _color; + // here changed. + // Background color is always opaque, i.e. no alpha channel. + color->AlphaChannel(false); +} + +FCTSetBackgroundColor::~FCTSetBackgroundColor() +{ + delete (color); +} + +void FCTSetBackgroundColor::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + // here changed. + color->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagSetBackgroundColor, body.Size(), &body); +} + +///////////////////////////////////////////////////////////////////////////////// +// -------- FCTUnparsedTag -------------------------------------------------- + +FCTUnparsedTag::FCTUnparsedTag(U16 _tagId, U32 _lenght, U8 *_data) +{ + lenght = _lenght; + if (lenght > 0) { + data = new U8[lenght]; + memcpy(data, _data, lenght); + } else + data = 0; + + tagId = _tagId; +} + +FCTUnparsedTag::~FCTUnparsedTag() +{ + delete data; +} + +void FCTUnparsedTag::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + // here changed. + if (lenght > 0) + body.WriteLargeData(data, lenght); + + _SWFStream->AppendTag(tagId, body.Size(), &body); +} + +USHORT FCTUnparsedTag::GetID() +{ + if (tagId == stagDefineButton2 || + tagId == stagDefineButton || + tagId == stagDefineText || + tagId == stagDefineText2 || + tagId == stagDefineEditText || + tagId == stagDefineMorphShape || + tagId == stagDefineBitsLossless2 || + tagId == stagDefineBitsLossless || + tagId == stagDefineBitsJPEG3) + return (USHORT)(data[0] | (data[1] << 8)); + return 0; +} + +void FCTUnparsedTag::SetID(USHORT id) +{ + if (tagId == stagDefineButton2 || + tagId == stagDefineButton || + tagId == stagDefineText || + tagId == stagDefineText2 || + tagId == stagDefineEditText || + tagId == stagDefineMorphShape || + tagId == stagDefineBitsLossless2 || + tagId == stagDefineBitsLossless || + tagId == stagDefineBitsJPEG3) { + data[0] = (id & 0xff); + data[1] = (id >> 8); + } +} + +///////////////////////////////////////////////////////////////////////////// +// -------- FCTShowFrame -------------------------------------------------- + +FCTShowFrame::FCTShowFrame() +{ +} + +U32 FCTShowFrame::IsShowFrame() +{ + + return 1; +} + +void FCTShowFrame::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->AppendTag(stagShowFrame, 0, 0); +} + +///////////////////////////////////////////////////////////////////////////// +// -------- FCTStartSound -------------------------------------------------- + +FCTStartSound::FCTStartSound(U16 _soundID, FSoundInfo *_soundInfo) +{ + + soundID = _soundID; + soundInfo = _soundInfo; +} + +FCTStartSound::~FCTStartSound() +{ + + delete soundInfo; +} + +void FCTStartSound::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)soundID); + soundInfo->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagStartSound, body.Size(), &body); +} diff --git a/toonz/sources/common/flash/FCT.h b/toonz/sources/common/flash/FCT.h new file mode 100644 index 0000000..f503ec6 --- /dev/null +++ b/toonz/sources/common/flash/FCT.h @@ -0,0 +1,302 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FCT.h + + This header-file contains the declarations of the low-level Control-Tag related classes: + + 1) class FCT; + 2) all the derived classes of class FCT: + + class FCTDoAction; + class FCTFrameLabel; + class FCTPlaceObject; + class FCTPlaceObject2; + class FCTProtect; + class FCTRemoveObject; + class FCTRemoveObject2; + class FCTSetBackgroundColor; + class FCTShowFrame; + class FCTStartSound; + + +****************************************************************************************/ + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TNZCORE_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif +#ifndef _F_C_T_H_ +#define _F_C_T_H_ + +#include "Macromedia.h" +#include "FObj.h" +#include "FAction.h" +#include "FPrimitive.h" + +class FCXForm; +class FCXFormWAlpha; + +class FSoundInfo; + +// A "control tag" type of flash object + +class DVAPI FCT : public FObj +{ +public: + virtual ~FCT() {} + virtual void WriteToSWFStream(FSWFStream * /*_SWFStream*/) {} +}; + +// flash object that directs the flash player to complete a given set of actions at frame completion + +class DVAPI FCTDoAction : public FCT +{ +public: + FCTDoAction(); + virtual ~FCTDoAction(); + + void AddAction(FActionRecord *_actionRecord); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + std::list actionRecordList; +}; + +// Associates a label with the frame. This label can then be used +// in the Go to Label Action. + +class DVAPI FCTFrameLabel : public FCT +{ + +public: + FCTFrameLabel(FString *_frameName); + virtual ~FCTFrameLabel(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FString *frameName; +}; + +// a flash object that directs the player to add an object to the display list (flash 1.0) + +class DVAPI FCTPlaceObject : public FCT +{ +public: + // if no color transform then _colorTransform argument must be null + FCTPlaceObject(U16 _characterID, + U16 _depth, + FMatrix *_matrix, // always present + FCXForm *_colorTransform); // NULL if there is not a color transform + virtual ~FCTPlaceObject(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + U16 depth; + FMatrix *matrix; + FCXForm *colorTransform; +}; + +class DVAPI FCTPlaceObject2 : public FCT +{ +public: + // if a certain flag is not true, you must provide a NULL value for its associated argument(s) + FCTPlaceObject2(U16 _hasClipDepth, /*DM*/ + U16 _hasRatio, + U16 _hasCharID, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, // NULL if the object does not have a matrix + FCXFormWAlpha *_colorTransform, // NULL if there is no color transform + U16 _ratio, + FString *_name, // NULL if there is no name + U16 _clipDepth); /*DM*/ + + //now is possible to give a clip action + // if a certain flag is not true, you must provide a NULL value for its associated argument(s) + FCTPlaceObject2(U16 _hasClipAction, + U16 _hasClipDepth, /*DM*/ + U16 _hasRatio, + U16 _hasCharID, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, // NULL if the object does not have a matrix + FCXFormWAlpha *_colorTransform, // NULL if there is no color transform + U16 _ratio, + FString *_name, // NULL if there is no name + U16 _clipDepth, + FClipAction *_clipAction); /*DM*/ + + FCTPlaceObject2(const FCTPlaceObject2 &_obj); + + void SetParameters(U16 _hasClipDepth, /*DM*/ + U16 _hasRatio, + U16 _hasCharID, + U16 _hasMove, + U16 _depth, + U16 _characterID, + FMatrix *_matrix, // NULL if the object does not have a matrix + FCXFormWAlpha *_colorTransform, // NULL if there is no color transform + U16 _ratio, + FString *_name, // NULL if there is no name + U16 _clipDepth); /*DM*/ + + // Used to fix the high level manager depth sorting problem. + int GetDepth() { return depth; } + void SetDepth(int d) { depth = d; } + + virtual ~FCTPlaceObject2(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + U32 IsPlaceObject(); + int GetPlacedId(); + std::string GetName() { return name ? name->GetString() : string(); } + void SetId(U16 id); + void ChangePlacedId(U16 id); + + void SetMatrix(FMatrix *matrix); + void ApplyMatrix(const FMatrix &_matrix) + { + if (matrix) + (*matrix) = (*matrix) * _matrix; + else + matrix = new FMatrix(_matrix); + } + +private: + //flags + U16 hasClipAction; + U16 hasClipDepth; /*DM*/ + U16 hasRatio; + U16 hasCharID; + U16 hasMove; + + U16 depth; + U16 characterID; + FMatrix *matrix; + FCXFormWAlpha *colorTransform; + U16 ratio; + FString *name; + U16 clipDepth; /*DM*/ + FClipAction *clipAction; +}; + +// a flash control tag object which marks a SWF movie as uneditable + +class DVAPI FCTProtect : public FCT +{ +public: + FCTProtect(); + + virtual void WriteToSWFStream(FSWFStream *_SWFStream); +}; + +// a Flash control tag object which indicates to the flash player to remove an object from the display list (flash 1.0) + +class DVAPI FCTRemoveObject : public FCT +{ + +public: + FCTRemoveObject(U16 _characterID, U16 _depth); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + + // Used to fix the high level manager depth sorting problem. + int GetDepth() { return depth; } + void SetDepth(int d) { depth = d; } + +private: + U16 characterID; + U16 depth; +}; + +// a Flash control tag object which indicates to the flash player to remove an object from the display list (flash 1.0) + +class DVAPI FCTRemoveObject2 : public FCT +{ + +public: + FCTRemoveObject2(U16 _depth); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 depth; +}; + +// A flash object which sets a movie's background color + +class DVAPI FCTSetBackgroundColor : public FCT +{ +public: + FCTSetBackgroundColor(FColor *_color); + virtual ~FCTSetBackgroundColor(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FColor *color; +}; + +class DVAPI FCTUnparsedTag : public FCT +{ +public: + FCTUnparsedTag(U16 _tagId, U32 _lenght, U8 *_data); + virtual ~FCTUnparsedTag(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + USHORT GetID(); + void SetID(USHORT id); + +private: + U8 *data; + U32 lenght; + U16 tagId; +}; + +//a control tag that indicates end of the current frame + +class DVAPI FCTShowFrame : public FCT +{ + +public: + FCTShowFrame(); + U32 IsShowFrame(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); +}; + +// A flash object that instructs flash player to start a sound + +class DVAPI FCTStartSound : public FCT +{ +public: + FCTStartSound(U16 _soundID, FSoundInfo *_soundInfo); + virtual ~FCTStartSound(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 soundID; + FSoundInfo *soundInfo; +}; + +#ifdef WIN32 // added from DV +#pragma warning(pop) +#endif + +#endif diff --git a/toonz/sources/common/flash/FDT.cpp b/toonz/sources/common/flash/FDT.cpp new file mode 100644 index 0000000..6a59a01 --- /dev/null +++ b/toonz/sources/common/flash/FDT.cpp @@ -0,0 +1,20 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDT.cpp + + This source file is empty, since class FDT is an low-level abstract class for all + Definition-Tags, and all the derived classes of class FDT are defined in other + FDTxxx.cpp files. + +****************************************************************************************/ + +#include "FDT.h" + +// return 1; +// +// } diff --git a/toonz/sources/common/flash/FDT.h b/toonz/sources/common/flash/FDT.h new file mode 100644 index 0000000..37e459a --- /dev/null +++ b/toonz/sources/common/flash/FDT.h @@ -0,0 +1,68 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDT.h + + This header-file contains the declaration of low-level class FDT. It is derived from + low-level class FObj, and also an abstract class from which all other low-level + FDTxxxx classes are derived. + +****************************************************************************************/ + +#ifndef F_D_T_H_ +#define F_D_T_H_ + +#include "FObj.h" + +// A "define type" flash object +// Flash objects are separated into define and control types +// distinction neccecary because in a flash frame, all define objects must come before control objects + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class DVAPI FDT : public FObj +{ +public: + virtual ~FDT() {} + virtual void WriteToSWFStream(FSWFStream * /*_SWFStream*/) {} + // Defines, used by the font system. Perhaps not the best place for them, but better than + // the global situation. lee@middlesoft + enum { + ShiftJIS = 1, + Unicode = 2, + ANSI = 3 + }; + + virtual U16 ID(void) + { + FLASHASSERT(0); + return 0; + } + virtual void SetId(U16 id) { FLASHASSERT(0); } +}; + +#ifdef WIN32 // added from DV +#pragma warning(pop) +#endif +#endif diff --git a/toonz/sources/common/flash/FDTBitmaps.cpp b/toonz/sources/common/flash/FDTBitmaps.cpp new file mode 100644 index 0000000..ded1425 --- /dev/null +++ b/toonz/sources/common/flash/FDTBitmaps.cpp @@ -0,0 +1,295 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTBitmaps.cpp + + This source file contains the definition for all low-level bitmap-related functions, + grouped by classes, which are all derived from class FDT, and related to bitmaps: + + + Class Member Function + + FDTDefineBits FDTDefineBits(U32, U8*); + U16 ID(void); + void WriteToSWFStream(FSWFStream*); + + FDTDefineBitsJPEG2 FDTDefineBitsJPEG2(U8*, U32); + U16 FDTDefineBitsJPEG2::ID(void); + void WriteToSWFStream(FSWFStream*); + + FDTDefineBitsJPEG3 FDTDefineBitsJPEG3(U8*, U32, U8*, U32); + U16 FDTDefineBitsJPEG3::ID(void); + void WriteToSWFStream(FSWFStream*); + + FDTDefineBitsLosslessBase FDTDefineBitsLosslessBase(U8, U8, U16, int, + void*, void*, bool); + void WriteToSWFStream(FSWFStream*); + + FDTDefineBitsLossless FDTDefineBitsLossless(U8, U16, U16, int, FRGB*, void*); + + FDTDefineBitsLossless2 FDTDefineBitsLossless(U8, U16, U16, int, FRGBA*, void*); + + FDTJPEGTables FDTJPEGTables(U32, U8*); + void WriteToSWFStream(FSWFStream*); + + + +****************************************************************************************/ + +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#include "FSWFStream.h" +#include "FObj.h" +#include "FDTBitmaps.h" +#include "zlib.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBits ---------------------------------------------------------- + +FDTDefineBits::FDTDefineBits(U32 _size, U8 *_image) +{ + size = _size; + image = _image; + characterID = FObjCollection::Increment(); +} + +U16 FDTDefineBits::ID(void) +{ + return characterID; +} + +void FDTDefineBits::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + body.WriteLargeData(image, size); + + _SWFStream->AppendTag(stagDefineBits, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBitsJPEG2 ----------------------------------------------------- + +FDTDefineBitsJPEG2::FDTDefineBitsJPEG2(U8 *_JPEGStream, U32 _JPEGSize) +{ + + JPEGStream = new U8[_JPEGSize]; + memcpy(JPEGStream, _JPEGStream, _JPEGSize); + JPEGSize = _JPEGSize; + characterID = FObjCollection::Increment(); +} + +U16 FDTDefineBitsJPEG2::ID(void) +{ + + return characterID; +} + +FDTDefineBitsJPEG2::~FDTDefineBitsJPEG2() +{ +} + +void FDTDefineBitsJPEG2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)characterID); + + // 2 bytes indicating end of encoding stream + // no encoding data is written here because it is an empty stream + body.WriteByte(0xff); + body.WriteByte(0xd9); + + //2 bytes indicating beginning of JPEG stream + body.WriteByte(0xff); + body.WriteByte(0xd8); + + //the entire JPEG stream + body.WriteLargeData(JPEGStream, JPEGSize); + + //2 bytes indicating end of JPEG stream + body.WriteByte(0xff); + body.WriteByte(0xd9); + + _SWFStream->AppendTag(stagDefineBitsJPEG2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBitsJPEG3 ----------------------------------------------------- + +FDTDefineBitsJPEG3::FDTDefineBitsJPEG3(U8 *_JPEGStream, U32 _JPEGSize, + U8 *_alphaStream, U32 _alphaSize) +{ + JPEGStream = _JPEGStream; + JPEGSize = _JPEGSize; + alphaStream = _alphaStream; + alphaSize = _alphaSize; + characterID = FObjCollection::Increment(); +} + +U16 FDTDefineBitsJPEG3::ID(void) +{ + return characterID; +} + +void FDTDefineBitsJPEG3::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)characterID); + + //offset includes the 2 end of stream tags and the 1 beginning stream tag + U32 offset = JPEGSize + 6; + body.WriteDWord(offset); + + // 2 bytes indicating end of encoding stream + // no encoding data is written here + // an empty stream is written + body.WriteByte(0xff); + body.WriteByte(0xd9); + + //2 bytes indicating begining of JPEG stream + body.WriteByte(0xff); + body.WriteByte(0xd8); + + //the entire JPEG stream + body.WriteLargeData(JPEGStream, JPEGSize); + + //2 bytes indicating end of JPEG steam + body.WriteByte(0xff); + body.WriteByte(0xd9); + + // alpha data + body.WriteLargeData(alphaStream, alphaSize); + + _SWFStream->AppendTag(stagDefineBitsJPEG3, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBitsLosslessBase ---------------------------------------------- + +FDTDefineBitsLosslessBase::FDTDefineBitsLosslessBase(U8 _format, + U16 _width, + U16 _height, + int _colorTableCount, + const void *_colorTableData, + const void *_imageData, + bool _alpha) +{ + format = _format; + width = _width; + height = _height; + colorTableCount = _colorTableCount; + alpha = _alpha; + characterID = FObjCollection::Increment(); + + // copy the memory to another block to be compressed + int rgbBytes = (alpha) ? 4 : 3; + int tableBytes = colorTableCount * rgbBytes; + + int bits = (1 << format); // how many bits does this format have? + + int imageBytes = (width * height * bits + 7) / 8; + TUINT32 totalBytes = imageBytes + tableBytes; + + // copy the image and the table to a new buffer + unsigned char *raw = new unsigned char[totalBytes]; + if (tableBytes) { + memcpy(raw, _colorTableData, tableBytes); + } + memcpy(&raw[tableBytes], _imageData, imageBytes); + + // a compressed buffer - the allocated size is based on a zlib formula + compressedSize = totalBytes + totalBytes / 100 + 12; + compressed = new unsigned char[compressedSize]; + + // now compress the raw data - note this will change compressedSize + int ret = compress2(compressed, (uLongf *)&compressedSize, raw, totalBytes, Z_BEST_COMPRESSION); + + FLASHASSERT(ret == Z_OK); + + delete[] raw; +} + +FDTDefineBitsLosslessBase::~FDTDefineBitsLosslessBase() +{ + delete[] compressed; +} + +void FDTDefineBitsLosslessBase::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + body.WriteByte((U32)format); + body.WriteWord((U32)width); + body.WriteWord((U32)height); + + if (format <= bm8Bit) { + body.WriteByte((U32)(colorTableCount - 1)); + } + + body.WriteLargeData(compressed, compressedSize); + + if (alpha) + _SWFStream->AppendTag(stagDefineBitsLossless2, body.Size(), &body); + else + _SWFStream->AppendTag(stagDefineBitsLossless, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBitsLossless -------------------------------------------------- + +FDTDefineBitsLossless::FDTDefineBitsLossless(U8 _format, + U16 _width, + U16 _height, + int _colorTableCount, + const FRGB *_colorTableData, + const void *_imageData) + : FDTDefineBitsLosslessBase(_format, _width, _height, _colorTableCount, _colorTableData, _imageData, false) +{ +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineBitsLossless2 ------------------------------------------------- + +FDTDefineBitsLossless2::FDTDefineBitsLossless2(U8 _format, + U16 _width, + U16 _height, + int _colorTableCount, + const FRGBA *_colorTableData, + const void *_imageData) + : FDTDefineBitsLosslessBase(_format, _width, _height, _colorTableCount, _colorTableData, _imageData, true) +{ +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTJPEGTables ---------------------------------------------------------- + +// Constructor. Currently takes in a U32 indicating the size of the data in bytes, and +// a pointer to the beginning of the stream of data. +FDTJPEGTables::FDTJPEGTables(U32 encodingDataSize, U8 *encodingData) +{ + this->encodingData = encodingData; + this->encodingDataSize = encodingDataSize; +} + +// Writes to the given _SWFStream. + +void FDTJPEGTables::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + body.WriteLargeData(encodingData, encodingDataSize); + + _SWFStream->AppendTag(stagJPEGTables, body.Size(), &body); +} diff --git a/toonz/sources/common/flash/FDTBitmaps.h b/toonz/sources/common/flash/FDTBitmaps.h new file mode 100644 index 0000000..f6afb61 --- /dev/null +++ b/toonz/sources/common/flash/FDTBitmaps.h @@ -0,0 +1,196 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTBitmaps.h + + This header-file contains the declarations of all low-level bitmap-related classes, + which are all derived from class FDT: + + class FDTDefineBits; + class FDTDefineBitsJPEG2; + class FDTDefineBitsJPEG3; + class FDTDefineBitsLosslessBase; + class FDTDefineBitsLossless : public FDTDefineBitsLosslessBase; + class FDTDefineBitsLossless2 : public FDTDefineBitsLosslessBase; + class FDTJPEGTables; + +****************************************************************************************/ + +#ifndef _F_DEFINE_BITMAPS_H_ +#define _F_DEFINE_BITMAPS_H_ + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "FDT.h" + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +struct FRGB; +struct FRGBA; + +// A flash object which defines a jpeg bitmap image (flash 1.0) + +class DVAPI FDTDefineBits : public FDT +{ + +public: + // constructed with image size and a pointer to the actual jpeg stream + FDTDefineBits(U32 _size, U8 *_image); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + U32 size; + U8 *image; +}; + +// A flash object which defines a jpeg bitmap image (flash 2.0) +// Differs from FDTDefineBits in that the encoding data and image data are contained in the object separately, but this DefineBitsJPEG2 doesn't really do this (as does not flash)... +// An empty stream is writen where encoding data should normally be encountered and all the JPEG and encoding data is written within the JPEG stream + +class DVAPI FDTDefineBitsJPEG2 : public FDT +{ + +public: + FDTDefineBitsJPEG2(U8 *_JPEGStream, U32 _JPEGSize); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + ~FDTDefineBitsJPEG2(); + +private: + U16 characterID; + U32 JPEGSize; + U8 *JPEGStream; +}; + +// A flash object which defines a jpeg bitmap image (flash 3.0) +// Differs from FDTDefineBitsJPEG2 in that Alpha transparency data is contained in this object + +class DVAPI FDTDefineBitsJPEG3 : public FDT +{ + +public: + FDTDefineBitsJPEG3(U8 *_JPEGStream, U32 _JPEGSize, + U8 *_alphaStream, U32 _alphaSize); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + U32 alphaSize; + U32 JPEGSize; + U8 *JPEGStream; + U8 *alphaStream; +}; + +// Base class for FDTDefineBitsLossless (below) and FDTDefineBitsLossless2. +// Please see those two classes for descripion of parameters. +// Note this class can be constructed - it will write the correct tag when +// WriteToSWFStream is called. Or one of the child classes can be used. + +class DVAPI FDTDefineBitsLosslessBase : public FDT +{ + +public: + enum { + bm1Bit, // 2 color + bm2Bit, // 4 color + bm4Bit, // 16 color + bm8Bit, // 256 color + bm16Bit, // high + bm32Bit // true + }; + + virtual U16 ID() { return characterID; } + + FDTDefineBitsLosslessBase(U8 _format, + U16 _width, + U16 _height, + int _colorTableCount, + const void *_colorTableData, + const void *_imageData, + bool alpha); + virtual ~FDTDefineBitsLosslessBase(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + U8 format; + U16 width; + U16 height; + int colorTableCount; + bool alpha; + TUINT32 compressedSize; // a count of how many bytes are in the compressed buffer + unsigned char *compressed; // pointer to the compressed data +}; + +// Defines a loss-less bitmap object, like a GIF, BMP, or PCT. +// This version does not accept alpha channel data - FDTDefineBitsLossless2 does. +// Accepts raw bitmap data and compresses it. + +class DVAPI FDTDefineBitsLossless : public FDTDefineBitsLosslessBase +{ + +public: + FDTDefineBitsLossless(U8 _format, // format, from FDTDefineBitsLosslessBase. + U16 _width, // size of the image + U16 _height, + int _colorTableCount, // how many entries in the color table - consistent + // with format. May be 0. + const FRGB *_colorTableData, // Null if no color cable. RGB data + const void *_imageData // Pointer to the image. (byte aligned.) + ); +}; + +// Defines a loss-less bitmap object, like a GIF, BMP, or PCT. +// This version requires alpha channel data. Note the color table is RGBA. +// Accepts raw bitmap data and compresses it. + +class DVAPI FDTDefineBitsLossless2 : public FDTDefineBitsLosslessBase +{ + +public: + FDTDefineBitsLossless2(U8 _format, // format, from FDTDefineBitsLosslessBase. + U16 _width, // size of the image + U16 _height, + int _colorTableCount, // how many entries in the color table - consistent + // with format. May be 0. + const FRGBA *_colorTableData, // Null if no color cable. RGB data + const void *_imageData // Pointer to the image. (byte aligned.) + ); +}; + +//the JPEGTable structure (contains the encoding scheme for all JPEGs defined using DefineBits + +class DVAPI FDTJPEGTables : public FDT +{ + +public: + FDTJPEGTables(U32 encodingDataSize, U8 *encodingData); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 encodingDataSize; + U8 *encodingData; +}; +#endif diff --git a/toonz/sources/common/flash/FDTButtons.cpp b/toonz/sources/common/flash/FDTButtons.cpp new file mode 100644 index 0000000..df6b692 --- /dev/null +++ b/toonz/sources/common/flash/FDTButtons.cpp @@ -0,0 +1,411 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTButtons.cpp + + This source file contains the definition for all low-level button-related functions, + grouped by classes: + + Class Member Function + + FButtonRecord1 FButtonRecord1(U8, U8, U8, U8, U16, FMatrix*); + FButtonRecord1 ~FButtonRecord1(); + void WriteToSWFStream(FSWFStream*); + + FButtonRecord2 FButtonRecord2(U8, U8, U8, U8, U16, U16, FMatrix*, FACXForm*); + ~FButtonRecord2(); + void WriteToSWFStream(FSWFStream*); + + FButtonRecordList FButtonRecordList(); + ~FButtonRecordList(); + void AddRecord(FAButtonRecord*); + int Size(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineButton FDTDefineButton(void); + ~FDTDefineButton(); + U16 ID(void); + void AddButtonRecord(FButtonRecord1*); + void AddActionRecord(FActionRecord*); + void WriteToSWFStream(FSWFStream*); + + FDTDefineButton2 FDTDefineButton2(U8); + ~FDTDefineButton2(void); + U16 ID(void); + void AddButtonRecord(FButtonRecord2*); + void AddActionCondition(FActionCondition*); + void WriteToSWFStream(FSWFStream*); + + FDTDefineButtonCXForm FDTDefineButtonCXForm(U16, FCXForm*); + ~FDTDefineButtonCXForm(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineButtonSound FDTDefineButtonSound(U16, U16, FSoundInfo*, U16, FSoundInfo*, + U16, FSoundInfo*, U16, FSoundInfo*); + ~FDTDefineButtonSound(); + void WriteToSWFStream(FSWFStream*); + +****************************************************************************************/ +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#include "FPrimitive.h" +#include "FDTButtons.h" +#include "FAction.h" +#include "FDTShapes.h" +#include "FDTSounds.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FButtonRecord1 --------------------------------------------------------- + +FButtonRecord1::FButtonRecord1(U8 _hit, U8 _down, U8 _over, U8 _up, U16 _layer, FMatrix *_matrix) +{ + hit = _hit; + down = _down; + over = _over; + up = _up; + layer = _layer; + matrix = _matrix; + characterID = FObjCollection::Increment(); +} + +FButtonRecord1::~FButtonRecord1() +{ + delete matrix; +} + +void FButtonRecord1::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteBits(0, 4); + _SWFStream->WriteBits(hit, 1); + _SWFStream->WriteBits(down, 1); + _SWFStream->WriteBits(over, 1); + _SWFStream->WriteBits(up, 1); + _SWFStream->WriteWord(characterID); + _SWFStream->WriteWord(layer); + matrix->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FButtonRecord2 --------------------------------------------------------- + +FButtonRecord2::FButtonRecord2(U8 _hit, U8 _down, U8 _over, U8 _up, U16 _characterID, U16 _layer, FMatrix *_matrix, FACXForm *_colorTransform) +{ + hit = _hit; + down = _down; + over = _over; + up = _up; + characterID = _characterID; + layer = _layer; + matrix = _matrix; + colorTransform = _colorTransform; +} + +FButtonRecord2::~FButtonRecord2() +{ + delete matrix; + delete colorTransform; +} + +void FButtonRecord2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->FlushBits(); + _SWFStream->WriteBits(0, 4); + _SWFStream->WriteBits(hit, 1); + _SWFStream->WriteBits(down, 1); + _SWFStream->WriteBits(over, 1); + _SWFStream->WriteBits(up, 1); + _SWFStream->WriteWord(characterID); + _SWFStream->WriteWord(layer); + + matrix->WriteToSWFStream(_SWFStream); + _SWFStream->FlushBits(); + colorTransform->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FButtonRecordList ------------------------------------------------------ + +FButtonRecordList::FButtonRecordList() {} + +FButtonRecordList::~FButtonRecordList() +{ + while (!listOfButtonRecords.empty()) { + + delete listOfButtonRecords.front(); + + listOfButtonRecords.pop_front(); + } +} + +void FButtonRecordList::AddRecord(FAButtonRecord *_buttonRecord) +{ + + listOfButtonRecords.push_back(_buttonRecord); +} + +int FButtonRecordList::Size() +{ + + return listOfButtonRecords.size(); +} + +void FButtonRecordList::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + std::list::iterator cursor; + + for (cursor = listOfButtonRecords.begin(); cursor != listOfButtonRecords.end(); cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineButton -------------------------------------------------------- + +FDTDefineButton::FDTDefineButton(void) +{ + + characterID = FObjCollection::Increment(); +} + +FDTDefineButton::~FDTDefineButton() +{ + + // delete all entries in action record list + while (!listOfActionRecords.empty()) { + + delete listOfActionRecords.front(); + listOfActionRecords.pop_front(); + } + + //delete all entries in button record list + while (!listOfButtonRecords.empty()) { + + delete listOfButtonRecords.front(); + + listOfButtonRecords.pop_front(); + } +} + +U16 FDTDefineButton::ID(void) +{ + + return characterID; +} + +void FDTDefineButton::AddButtonRecord(FButtonRecord1 *_buttonRecord) +{ + + listOfButtonRecords.push_back(_buttonRecord); +} + +void FDTDefineButton::AddActionRecord(FActionRecord *_actionRecord) +{ + + listOfActionRecords.push_back(_actionRecord); +} + +void FDTDefineButton::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + body.WriteWord((U32)characterID); + + //write the button record list to the body + std::list::iterator cursor; + + for (cursor = listOfButtonRecords.begin(); cursor != listOfButtonRecords.end(); cursor++) { + + (*cursor)->WriteToSWFStream(&body); + } + + //write the end of button record flag + body.WriteByte(0); + + //write the action record list to the body + std::list::iterator cursor2; + for (cursor2 = listOfActionRecords.begin(); cursor2 != listOfActionRecords.end(); cursor2++) { + + (*cursor2)->WriteToSWFStream(&body); + } + + //write the action end flag + body.WriteByte(0); + + _SWFStream->AppendTag(stagDefineButton2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineButton2 ------------------------------------------------------- + +FDTDefineButton2::FDTDefineButton2(U8 _menuFlag) +{ + + characterID = FObjCollection::Increment(); + menuFlag = _menuFlag; +} + +FDTDefineButton2::~FDTDefineButton2(void) +{ + + //delete all entries in conditionsList + while (!conditionList.empty()) { + + delete conditionList.front(); + + conditionList.pop_front(); + } + + //delete all entries in listOfButtonRecords + while (!listOfButtonRecords.empty()) { + + delete listOfButtonRecords.front(); + + listOfButtonRecords.pop_front(); + } +} + +U16 FDTDefineButton2::ID(void) +{ + + return characterID; +} + +void FDTDefineButton2::AddButtonRecord(FButtonRecord2 *_buttonRecord) +{ + + listOfButtonRecords.push_back(_buttonRecord); +} + +void FDTDefineButton2::AddActionCondition(FActionCondition *_actionCondition) +{ + + conditionList.push_back(_actionCondition); +} + +void FDTDefineButton2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)characterID); + + body.WriteByte((U32)menuFlag); + + FSWFStream buttonRecordStream; + + //write the list of button records to button record stream + std::list::iterator cursor; + + for (cursor = listOfButtonRecords.begin(); cursor != listOfButtonRecords.end(); cursor++) { + + (*cursor)->WriteToSWFStream(&buttonRecordStream); + } + + buttonRecordStream.WriteByte((U32)0); + U32 offset = 0; + if (!conditionList.empty()) + offset = buttonRecordStream.Size() + 2; //have to count the action offset also + body.WriteWord(offset); + body.Append(&buttonRecordStream); + + //write the list of action records + if (!conditionList.empty()) { + std::list::iterator cursor1; + std::list::iterator cursor2; + cursor2 = (conditionList.end()); + cursor2--; + + for (cursor1 = conditionList.begin(); cursor1 != cursor2; cursor1++) { + + (*cursor1)->WriteToSWFStream(&body); + } + + (*cursor1)->WriteToSWFStream(&body, 1); //flag indicating it is the last action condition + } + + _SWFStream->AppendTag(stagDefineButton2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineButtonCXForm -------------------------------------------------- + +FDTDefineButtonCXForm::FDTDefineButtonCXForm(U16 _characterID, FCXForm *_colorTransform) +{ + characterID = _characterID; + colorTransform = _colorTransform; +} + +FDTDefineButtonCXForm::~FDTDefineButtonCXForm() +{ + + delete colorTransform; +} + +void FDTDefineButtonCXForm::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)characterID); + colorTransform->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagDefineButtonCxform, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineButtonSound --------------------------------------------------- + +FDTDefineButtonSound::FDTDefineButtonSound(U16 _buttonID, U16 _soundID0, FSoundInfo *_soundInfo0, + U16 _soundID1, FSoundInfo *_soundInfo1, + U16 _soundID2, FSoundInfo *_soundInfo2, + U16 _soundID3, FSoundInfo *_soundInfo3) +{ + buttonID = _buttonID; + soundID0 = _soundID0; + soundInfo0 = _soundInfo0; + soundID1 = _soundID1; + soundInfo1 = _soundInfo1; + soundID2 = _soundID2; + soundInfo2 = _soundInfo2; + soundID3 = _soundID3; + soundInfo3 = _soundInfo3; +} + +FDTDefineButtonSound::~FDTDefineButtonSound() +{ + + delete soundInfo0; + delete soundInfo1; + delete soundInfo2; + delete soundInfo3; +} + +void FDTDefineButtonSound::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)buttonID); + + body.WriteWord((U32)soundID0); + soundInfo0->WriteToSWFStream(&body); + + body.WriteWord((U32)soundID1); + soundInfo1->WriteToSWFStream(&body); + + body.WriteWord((U32)soundID2); + soundInfo2->WriteToSWFStream(&body); + + body.WriteWord((U32)soundID3); + soundInfo3->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagDefineButtonSound, body.Size(), &body); +} diff --git a/toonz/sources/common/flash/FDTButtons.h b/toonz/sources/common/flash/FDTButtons.h new file mode 100644 index 0000000..d2d2c3e --- /dev/null +++ b/toonz/sources/common/flash/FDTButtons.h @@ -0,0 +1,201 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTButtons.h + + This header-file contains the declarations of all low-level button-related classes. + Their parent classes are in the parentheses: + + class FAButtonRecord; + class FButtonRecord1; (public FAButtonRecord) + class FButtonRecord2; (public FAButtonRecord) + class FButtonRecordList; + class FDTDefineButton; (public FDT) + class FDTDefineButton2; (public FDT) + class FDTDefineButtonCXForm; (public FDT) + class FDTDefineButtonSound. (public FDT) + +****************************************************************************************/ + +#ifndef _F_DTBUTTONS_H_ +#define _F_DTBUTTONS_H_ + +#include "FDT.h" + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class FMatrix; +class FACXForm; +class FActionRecord; +class FActionCondition; +class FCXForm; +class FSoundInfo; + +// Specifies appearance aspects for a button definition + +class DVAPI FAButtonRecord +{ + +public: + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; + + virtual ~FAButtonRecord() {} +}; + +// Specifies appearance aspects for a button definition (flash 1.0) + +class DVAPI FButtonRecord1 : public FAButtonRecord +{ + +public: + FButtonRecord1(U8 _hit, U8 _down, U8 _over, U8 _up, U16 _layer, FMatrix *_matrix); + virtual ~FButtonRecord1(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 hit; + U8 down; + U8 over; + U8 up; + U16 layer; + FMatrix *matrix; + U16 characterID; +}; + +// Specifies appearance aspects for a button definition (flash 3.0) + +class DVAPI FButtonRecord2 : public FAButtonRecord +{ + +public: + FButtonRecord2(U8 _hit, U8 _down, U8 _over, U8 _up, U16 _characterID, U16 _layer, FMatrix *_matrix, FACXForm *_colorTransform); + virtual ~FButtonRecord2(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 hit; + U8 down; + U8 over; + U8 up; + U16 layer; + FMatrix *matrix; + FACXForm *colorTransform; + U16 characterID; +}; + +// a list of button records + +class DVAPI FButtonRecordList +{ + +public: + FButtonRecordList(); + virtual ~FButtonRecordList(); + void AddRecord(FAButtonRecord *_buttonRecord); + int Size(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + std::list listOfButtonRecords; +}; + +// a flash object that defines a button in a SWF movie (flash 1.0) + +class DVAPI FDTDefineButton : public FDT +{ + +public: + FDTDefineButton(void); + virtual ~FDTDefineButton(); + U16 ID(void); + void AddButtonRecord(FButtonRecord1 *_buttonRecord); + void AddActionRecord(FActionRecord *_actionRecord); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + std::list listOfActionRecords; + std::list listOfButtonRecords; +}; + +// a flash object that defines a button in a SWF movie (flash 3.0) + +class DVAPI FDTDefineButton2 : public FDT +{ + +public: + FDTDefineButton2(U8 _menuFlag); + virtual ~FDTDefineButton2(void); + U16 ID(void); + void AddButtonRecord(FButtonRecord2 *_buttonRecord); + void AddActionCondition(FActionCondition *_actionCondition); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + virtual void SetID(U16 id) { characterID = id; } + +private: + U16 characterID; + U8 menuFlag; + std::list conditionList; + std::list listOfButtonRecords; +}; + +//A flash object that defines a color transformation on a button + +class DVAPI FDTDefineButtonCXForm : public FDT +{ + +public: + FDTDefineButtonCXForm(U16 _characterID, FCXForm *_colorTransform); + virtual ~FDTDefineButtonCXForm(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + FCXForm *colorTransform; +}; + +class DVAPI FDTDefineButtonSound : public FDT +{ + +public: + FDTDefineButtonSound(U16 _buttonID, U16 _soundID0, FSoundInfo *_soundInfo0, + U16 _soundID1, FSoundInfo *_soundInfo1, + U16 _soundID2, FSoundInfo *_soundInfo2, + U16 _soundID3, FSoundInfo *_soundInfo3); + virtual ~FDTDefineButtonSound(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 buttonID; + U16 soundID0; + U16 soundID1; + U16 soundID2; + U16 soundID3; + FSoundInfo *soundInfo0; + FSoundInfo *soundInfo1; + FSoundInfo *soundInfo2; + FSoundInfo *soundInfo3; +}; + +#endif diff --git a/toonz/sources/common/flash/FDTFonts.cpp b/toonz/sources/common/flash/FDTFonts.cpp new file mode 100644 index 0000000..c03af60 --- /dev/null +++ b/toonz/sources/common/flash/FDTFonts.cpp @@ -0,0 +1,486 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTFonts.cpp + + This source file contains the definition for all low-level font-related functions, + grouped by classes: + + Class Member Function + + FDTDefineFont FDTDefineFont(); + ~FDTDefineFont(); + U16 ID(); + void AddShapeGlyph(FShape*); + void WriteToSWFStream(FSWFStream*); + + FDTDefineFont2 FDTDefineFont2(char*, U16, U16, U16); + FDTDefineFont2(char*, U16, U16, U16, S16, S16, S16); + ~FDTDefineFont2(); + void AddShapeGlyph(FShape*, U16, S16, FRect*); + void AddKerningRec(FKerningRec*); + U16 nIndexBits(); + U16 ID(void); + void WriteToSWFStream(FSWFStream*); + + FDTDefineFontInfo FDTDefineFontInfo(const char*, U16, U16, U16, U16); + void FDTDefineFontInfo::AddCode(U16); + U16 FDTDefineFontInfo::ID(); + void WriteToSWFStream(FSWFStream*); + +// FGlyphEntry FGlyphEntry(U16, S16); +// S16 AdvanceValue(); +// void IncludeNBitInfo(U16, U16); +// void WriteToSWFStream(FSWFStream*); + + FKerningRec FKerningRec (U16, U16, short); + void CodesWide (U16); + void WriteToSWFStream(FSWFStream*); + + + Note: All member functions of FGlyphEntry have been commented out. Need to fix. + +****************************************************************************************/ +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#include "FDTFonts.h" +#include "FDTShapes.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FButtonRecord1 --------------------------------------------------------- + +FDTDefineFont::FDTDefineFont() +{ + + characterID = FObjCollection::Increment(); + + nFillBits = 1; + nLineBits = 0; +} + +FDTDefineFont::~FDTDefineFont() +{ + + while (!shapeGlyphs.empty()) { + + delete shapeGlyphs.front(); + shapeGlyphs.pop_front(); + } +} + +U16 FDTDefineFont::ID() +{ + + return (U8)characterID; +} + +void FDTDefineFont::AddShapeGlyph(FShape *_shape) +{ + + _shape->SetFillBits(nFillBits); + _shape->SetLineBits(nLineBits); + shapeGlyphs.push_back(_shape); +} + +void FDTDefineFont::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + U32 offsetsBufferSize = shapeGlyphs.size() * 2; + + FSWFStream body; + FSWFStream shapeBuffer; + std::list offsetsList; + + // get values for offsets and place them in a list + // write list of shapeGlyphs to shape buffer + offsetsList.push_back(offsetsBufferSize); + + std::list::iterator cursor; + std::list::iterator nextToLast = shapeGlyphs.end(); + nextToLast--; + for (cursor = shapeGlyphs.begin(); cursor != nextToLast; cursor++) { + + (*cursor)->WriteToSWFStream(&shapeBuffer); + offsetsList.push_back(offsetsBufferSize + shapeBuffer.Size()); + } + + (*cursor)->WriteToSWFStream(&shapeBuffer); + + body.WriteWord((U32)characterID); + + // write offsetsList to body + while (!offsetsList.empty()) { + + body.WriteWord((U32)offsetsList.front()); + offsetsList.pop_front(); + } + + //write shape buffer to body + body.Append(&shapeBuffer); + + //put tag on body and write to stream + _SWFStream->AppendTag(stagDefineFont, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineFont2 --------------------------------------------------------- + +FDTDefineFont2::FDTDefineFont2(const char *_fontName, U16 _encodeType, U16 _italicFlag, U16 _boldFlag) +{ + fontID = FObjCollection::Increment(); + hasLayoutFlag = 1; + encodeType = _encodeType; + italicFlag = _italicFlag; + boldFlag = _boldFlag; + fontName = new FString((U8 *)_fontName); + nFillBits = 1; + nLineBits = 0; + ascenderHeight = 0; + descenderHeight = 0; + leadingHeight = 0; +} + +FDTDefineFont2::FDTDefineFont2(const char *_fontName, U16 _encodeType, U16 _italicFlag, + U16 _boldFlag, S16 _ascenderHeight, + S16 _descenderHeight, S16 _leadingHeight) +{ + + fontID = FObjCollection::Increment(); + hasLayoutFlag = 1; + encodeType = _encodeType; + italicFlag = _italicFlag; + boldFlag = _boldFlag; + fontName = new FString((U8 *)_fontName); + + ascenderHeight = _ascenderHeight; + descenderHeight = _descenderHeight; + leadingHeight = _leadingHeight; + + nFillBits = 1; + nLineBits = 0; +} + +FDTDefineFont2::~FDTDefineFont2() +{ + + delete fontName; + + while (!glyphs.empty()) { + delete glyphs.front().shape; + delete glyphs.front().bounds; + glyphs.pop_front(); + } + + while (!kerningTable.empty()) { + + delete kerningTable.front(); + kerningTable.pop_front(); + } +} + +void FDTDefineFont2::AddShapeGlyph(FShape *_shape, U16 _shapeCode, S16 _shapeAdvance, + FRect *_shapeBounds) +{ + // FIXME, don't know what nFillBits and nLineBits are + _shape->SetFillBits(nFillBits); + _shape->SetLineBits(nLineBits); + Glyph g; + g.advance = _shapeAdvance; + g.bounds = _shapeBounds; + g.code = _shapeCode; + g.shape = _shape; + glyphs.push_back(g); +} + +void FDTDefineFont2::AddKerningRec(FKerningRec *_kerningRecord) +{ + + if (hasLayoutFlag) + kerningTable.push_back(_kerningRecord); +} + +U16 FDTDefineFont2::nIndexBits() +{ + + return (U16)FSWFStream::MinBits(glyphs.size() - 1, false); +} + +U16 FDTDefineFont2::ID(void) +{ + + return fontID; +} + +void FDTDefineFont2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + // U32 offsetsSize = (glyphs.size() * 2); + // U32 offsetsSizeWide = glyphs.size() * 4; + // U16 wideOffsetsFlag = 0; + // U16 wideCodesFlag = 0; + // std::list offsetsList; + + // FIXME: add wide offset later + S16 *offsetTable; + if (glyphs.size() > 0) + offsetTable = new S16[glyphs.size()]; + else + offsetTable = 0; + + int i; + + FSWFStream body; + FSWFStream shapeBuffer; + + std::list::iterator glyphCursor; + for (glyphCursor = glyphs.begin(), i = 0; glyphCursor != glyphs.end(); glyphCursor++, i++) { + offsetTable[i] = (S8)shapeBuffer.Size(); + glyphCursor->shape->WriteToSWFStream(&shapeBuffer); + } + + body.WriteWord((U32)fontID); + //write flags to body + body.WriteBits((U32)hasLayoutFlag, 1); + + switch (encodeType) { + + case ShiftJIS: + body.WriteBits((U32)1, 1); + body.WriteBits((U32)0, 1); + body.WriteBits((U32)0, 1); + break; + case Unicode: + body.WriteBits((U32)0, 1); + body.WriteBits((U32)1, 1); + body.WriteBits((U32)0, 1); + break; + case ANSI: + body.WriteBits((U32)0, 1); + body.WriteBits((U32)0, 1); + body.WriteBits((U32)1, 1); + break; + } + body.WriteBits((U32)0, 1); // 0 for narrowOffsetFlag + body.WriteBits((U32)0, 1); // 0 for narrowOffsetCode + body.WriteBits((U32)italicFlag, 1); + body.WriteBits((U32)boldFlag, 1); + body.WriteByte(0); // FontFlagsReserved UB[8] + + body.WriteByte((U32)fontName->Length()); + fontName->WriteToSWFStream(&body, false); // write the name + + body.WriteWord((U32)glyphs.size()); + + // write offsetsList to body + if (glyphs.size() > 0) + body.WriteLargeData((U8 *)offsetTable, 2 * glyphs.size()); + + //write shape glyph buffer to body + body.Append(&shapeBuffer); + + for (glyphCursor = glyphs.begin(), i = 0; glyphCursor != glyphs.end(); glyphCursor++, i++) { + body.WriteWord(glyphCursor->code); + } + + // write layout information to body + if (hasLayoutFlag) { + body.WriteWord((U32)ascenderHeight); + body.WriteWord((U32)descenderHeight); + body.WriteWord((U32)leadingHeight); + + for (glyphCursor = glyphs.begin(), i = 0; glyphCursor != glyphs.end(); glyphCursor++, i++) { + body.WriteWord(glyphCursor->advance); + } + + for (glyphCursor = glyphs.begin(), i = 0; glyphCursor != glyphs.end(); glyphCursor++, i++) { + glyphCursor->bounds->WriteToSWFStream(&body); + } + + body.WriteWord((U32)kerningTable.size()); + + std::list::iterator kernCursor; + + for (kernCursor = kerningTable.begin(); kernCursor != kerningTable.end(); kernCursor++) { + + (*kernCursor)->CodesWide(0); // 0 is narraw offset flag + (*kernCursor)->WriteToSWFStream(&body); + } + } + + // create entire tag with record header + _SWFStream->AppendTag(stagDefineFont2, body.Size(), &body); + delete[] offsetTable; +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FButtonRecord1 --------------------------------------------------------- + +FDTDefineFontInfo::FDTDefineFontInfo(const char *_fontName, U16 _fontID, + U16 _encodeType, U16 _italicFlag, + U16 _boldFlag) +{ + + characterID = FObjCollection::Increment(); + encodeType = _encodeType; + italicFlag = _italicFlag; + boldFlag = _boldFlag; + + fontID = _fontID; + + fontName = new FString((U8 *)_fontName); +} + +FDTDefineFontInfo::~FDTDefineFontInfo() +{ + delete fontName; +} + +void FDTDefineFontInfo::AddCode(U16 _someCode) +{ + + codeTable.push_back(_someCode); +} + +U16 FDTDefineFontInfo::ID() +{ + return (U8)characterID; +} + +void FDTDefineFontInfo::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + U16 wideCodesFlag = 0; + FSWFStream body; + + //determine whether 8 or 16 bit code fields are needed + std::list::iterator cursor; + for (cursor = codeTable.begin(); (cursor != codeTable.end()) && (wideCodesFlag == 0) // fixed from DV + ; + cursor++) { + if ((*cursor) > 65530) + wideCodesFlag = 1; + } + + body.WriteWord((U32)fontID); + + body.WriteByte((U32)fontName->Length()); + + fontName->WriteToSWFStream(&body, false); + + // body.WriteBits((U32) reservedFlags, 2); + body.WriteBits((U32)0, 2); + + switch (encodeType) { + + case ShiftJIS: + body.WriteBits((U32)0, 1); + body.WriteBits((U32)1, 1); + body.WriteBits((U32)0, 1); + break; + case Unicode: + body.WriteBits((U32)1, 1); + body.WriteBits((U32)0, 1); + body.WriteBits((U32)0, 1); + break; + case ANSI: + body.WriteBits((U32)0, 1); + body.WriteBits((U32)0, 1); + body.WriteBits((U32)1, 1); + break; + } + + body.WriteBits((U32)italicFlag, 1); + body.WriteBits((U32)boldFlag, 1); + + body.WriteBits((U32)wideCodesFlag, 1); + + //write code table to body + while (!codeTable.empty()) { + if (wideCodesFlag) { + body.WriteWord((U32)codeTable.front()); + codeTable.pop_front(); + } else { + body.WriteByte((U32)codeTable.front()); + codeTable.pop_front(); + } + } + + // create entire tag with record header + _SWFStream->AppendTag(stagDefineFontInfo, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FGlyphEntry ------------------------------------------------------------ + +// FGlyphEntry::FGlyphEntry(U16 index, S16 advance) +// { +// +// glyphIndex = index; +// glyphAdvance = advance; +// +// } +// +// +// S16 FGlyphEntry::AdvanceValue() +// { +// +// return glyphAdvance; +// +// } + +// Used to specify the nBit info for the entries. This is determined and passed to +// the glyph entry just before write time. + +// void FGlyphEntry::IncludeNBitInfo(U16 _nIndexBits, U16 _nAdvanceBits) +// { +// +// nIndexBits = _nIndexBits; +// nAdvanceBits = _nAdvanceBits; +// } +// +// void FGlyphEntry::WriteToSWFStream(FSWFStream *_SWFStream) +// { +// +// _SWFStream->WriteBits((U32) glyphIndex, nIndexBits); +// _SWFStream->WriteBits((U32) glyphAdvance, nAdvanceBits); +// +// } + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FKerningRec ------------------------------------------------------------ + +FKerningRec::FKerningRec(U16 cd1, U16 cd2, short krnAdj) +{ + + wideCodesFlag = 0; // default not wide + code1 = cd1; + code2 = cd2; + kerningAdjust = krnAdj; +} + +void FKerningRec::CodesWide(U16 _flag) +{ + + if (_flag) + wideCodesFlag = 1; + else + wideCodesFlag = 0; +} + +void FKerningRec::WriteToSWFStream(FSWFStream *b) +{ + if (wideCodesFlag) { + b->WriteWord((U32)code1); + b->WriteWord((U32)code2); + } else { + b->WriteByte((U32)code1); + b->WriteByte((U32)code2); + } + + b->WriteWord((U32)kerningAdjust); +} diff --git a/toonz/sources/common/flash/FDTFonts.h b/toonz/sources/common/flash/FDTFonts.h new file mode 100644 index 0000000..5b64924 --- /dev/null +++ b/toonz/sources/common/flash/FDTFonts.h @@ -0,0 +1,189 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTFonts.h + + This header-file contains the declarations of all low-level font-related classes. + Their parent classes are in the parentheses: + + class FKerningRec; + class FDTDefineFont; (public FDT) + class FDTDefineFont2; (public FDT) + class FDTDefineFontInfo; (public FDT) +// class FGlyphEntry; + + Note: Class FGlyphEntry has been commented out. Need to fix. + +****************************************************************************************/ + +#ifndef _FDT_FONTS_H_ +#define _FDT_FONTS_H_ + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" +#include "FDT.h" +#include "FPrimitive.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class FShape; + +// A kerning record + +class FKerningRec +{ + +public: + FKerningRec(U16 _code1, U16 _code2, S16 _kerningAdjust); + void CodesWide(U16 _flag); + virtual void WriteToSWFStream(FSWFStream *b); + + virtual ~FKerningRec() {} + +private: + U16 wideCodesFlag; + U16 code1; + U16 code2; + S16 kerningAdjust; +}; + +// A flash object that defines a font's appearance + +class DVAPI FDTDefineFont : public FDT +{ + +public: + FDTDefineFont(void); + virtual ~FDTDefineFont(); + + U16 ID(void); + void AddShapeGlyph(FShape *_shape); + int NumberOfGlyphs() { return shapeGlyphs.size(); } + + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 characterID; + std::list shapeGlyphs; + U32 nFillBits; + U32 nLineBits; +}; + +// A flash object that defines a font's appearance (flash 3.0) + +class DVAPI FDTDefineFont2 : public FDT +{ +public: + FDTDefineFont2(const char *_fontName, + U16 _encodeType, // ShiftJIS, Unicode, ANSI + U16 _italicFlag, + U16 _boldFlag); + + FDTDefineFont2(const char *_fontName, + U16 _encodeType, + U16 _italicFlag, + U16 _boldFlag, + S16 _ascenderHeight, + S16 _descenderHeight, + S16 _leadingHeight); + virtual ~FDTDefineFont2(); + + void AddShapeGlyph(FShape *_shape, U16 _shapeCode, S16 _shapeAdvance = 0, + FRect *_shapeBounds = 0); + void AddKerningRec(FKerningRec *_kerningRecord); + U16 nIndexBits(); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 fontID; + int hasLayoutFlag; + U16 encodeType; + int italicFlag; + int boldFlag; + + FString *fontName; + struct Glyph { + FShape *shape; + U16 code; + S16 advance; + FRect *bounds; + }; + S16 ascenderHeight; + S16 descenderHeight; + S16 leadingHeight; + + std::list glyphs; + + std::list kerningTable; + U32 nFillBits; + U32 nLineBits; +}; + +// A flash object that defines the mapping from a flash font object to a TrueType or ATM font so that a player can optionally use them + +class DVAPI FDTDefineFontInfo : public FDT +{ + +public: + FDTDefineFontInfo(const char *_fontName, + U16 _fontID, + U16 _encodeType, + U16 _italicFlag, + U16 _boldFlag); + virtual ~FDTDefineFontInfo(); + void AddCode(U16 _someCode); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 characterID; + FString *fontName; + U16 encodeType; + U16 italicFlag; + U16 boldFlag; + std::list codeTable; + U16 fontID; +}; + +// Found in DefineText. Used to describe the glyph index and X advance value to use for a +// particular character in the currently selected font for the text record. + +// class FGlyphEntry { +// +// public: +// +// FGlyphEntry (U16 index, S16 advance); +// S16 AdvanceValue(); +// void IncludeNBitInfo(U16 _nIndexBits, U16 _nAdvanceBits); +// void WriteToSWFStream(FSWFStream *_SWFStream); +// +// +// private: +// +// U16 glyphIndex; +// S16 glyphAdvance; +// U16 nIndexBits; +// U16 nAdvanceBits; +// +// }; + +#endif diff --git a/toonz/sources/common/flash/FDTShapes.cpp b/toonz/sources/common/flash/FDTShapes.cpp new file mode 100644 index 0000000..381f692 --- /dev/null +++ b/toonz/sources/common/flash/FDTShapes.cpp @@ -0,0 +1,1771 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTShapes.cpp + + This source file contains the definition for all low-level shape-related functions, + grouped by classes: + + Class Member Function + + FCXForm FCXForm(U32, U32, S32, S32, S32, S32, S32, S32); + U32 FCXForm::MinBits(); + void WriteToSWFStream(FSWFStream*); + + FCXFormWAlpha FCXFormWAlpha(U32, U32, S32, S32, S32, S32, S32, S32, S32, S32); + U32 MinBits(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineMorphShape FDTDefineMorphShape (FRect*, FRect*); + ~FDTDefineMorphShape(); + U32 AddFillStyle(FMorphFillStyle*); + U32 AddLineStyle(U32, FColor*, + U32, FColor*); + void FinishStyleArrays(); + void AddShapeRec_1(FShapeRec*); + void AddEdgeRec_2(FShapeRec*); + U16 ID(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineShape FDTDefineShape(FRect*); + ~FDTDefineShape(); + void AddShapeRec(FShapeRec*); + U32 AddFillStyle(FFillStyle*); + U32 AddSolidFillStyle(FColor*); + U32 AddLineStyle(U32, FColor*); + void FinishStyleArrays(); + U16 ID(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineShape2 FDTDefineShape2(FRect*); + ~FDTDefineShape2(); + void AddShapeRec(FShapeRec*); + U32 AddFillStyle(FFillStyle*); + U32 AddSolidFillStyle(FColor*); + U32 AddLineStyle(U32, FColor*); + void FinishStyleArrays(); + U16 ID(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineShape3 FDTDefineShape3(FRect*); + ~FDTDefineShape3(); + void AddShapeRec(FShapeRec*); + U32 AddFillStyle(FFillStyle*); + U32 AddSolidFillStyle(FColor*); + U32 AddLineStyle(U32, FColor*); + void FinishStyleArrays(); + U16 ID(); + void WriteToSWFStream(FSWFStream*); + + FFillStyleArray U32 Size(); + U32 Add(FAFillStyle*); + void WriteToSWFStream(FSWFStream*); + + FFillStyleBitmap FFillStyleBitmap(int, U16, FMatrix*); + ~FFillStyleBitmap(); + void WriteToSWFStream(FSWFStream*); + + FFillStyleGradient FFillStyleGradient(int, FMatrix*, FGradient*); + ~FFillStyleGradient(); + void WriteToSWFStream(FSWFStream*); + + FFillStyleSolid FFillStyleSolid(FColor*); + ~FFillStyleSolid(); + void WriteToSWFStream(FSWFStream*); + + FGradient FGradient(void); + ~FGradient(void); + void Add(FAGradRecord*); + void WriteToSWFStream(FSWFStream*); + + FGradRecord FGradRecord(U32, FColor*); + ~FGradRecord(); + void WriteToSWFStream(FSWFStream*); + + FLineStyle FLineStyle(U32, FColor*); + ~FLineStyle(); + void WriteToSWFStream(FSWFStream*); + + FLineStyleArray FLineStyleArray(); + ~FLineStyleArray(); + U32 Size(); + U32 Add(FALineStyle *); + void WriteToSWFStream(FSWFStream *); + + FMorphFillStyleBitmap FMorphFillStyleBitmap(int, U16, FMatrix*, FMatrix*); + ~FMorphFillStyleBitmap(); + void WriteToSWFStream(FSWFStream *); + + FMorphFillStyleGradient FMorphFillStyleGradient(int, FMatrix*, FMatrix*, FGradient*); + ~FMorphFillStyleGradient(); + void WriteToSWFStream(FSWFStream *); + + FMorphFillStyleSolid FMorphFillStyleSolid( FColor*, FColor*); + ~FMorphFillStyleSolid() + void WriteToSWFStream(FSWFStream *); + + FMorphGradRecord FMorphGradRecord(U32, FColor*, U32, FColor*); + ~FMorphGradRecord(); + void WriteToSWFStream(FSWFStream*); + + FMorphLineStyle FMorphLineStyle(U32, U32, FColor*, FColor*); + ~FMorphLineStyle(); + void WriteToSWFStream(FSWFStream *); + + FShape FShape(); + ~FShape(); + SetFillBits(U32); + SetLineBits(U32); + AddShapeRec(FShapeRec *); + void WriteToSWFStream(FSWFStream *); + + FShapeRecChange FShapeRecChange(U32, U32, U32, U32, U32, + S32, S32, U32, U32, U32, + FFillStyleArray*, FLineStyleArray*); + ~FShapeRecChange(); + void IncludeNFillBitInfo(U32); + void IncludeNLineBitInfo(U32); + U32 MinBits(); + void WriteToSWFStream(FSWFStream*); + + FShapeRecEdgeStraight FShapeRecEdgeStraight(S32, S32); + U32 MinBits(void); + void IncludeNFillBitInfo(U32); + void IncludeNLineBitInfo(U32); + void WriteToSWFStream(FSWFStream*); + + FShapeRecEdgeCurved FShapeRecEdgeCurved(S32, S32, S32, S32); + U32 MinBits(void); + void IncludeNFillBitInfo(U32 ); + void IncludeNLineBitInfo(U32 ); + void WriteToSWFStream(FSWFStream*); + + FShapeRecEnd FShapeRecEnd(); + void IncludeNFillBitInfo(U32 ); + void IncludeNLineBitInfo(U32 ); + void WriteToSWFStream(FSWFStream*); + + FShapeWStyle FShapeWStyle(FFillStyleArray*, FLineStyleArray*); + ~FShapeWStyle() ; + void AddShapeRec(FShapeRec*); + U32 NumFillBits(); + U32 NumLineBits(); + void WriteToSWFStream(FSWFStream*); + +****************************************************************************************/ + +#include "tpixel.h" +#include "FDTShapes.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCXForm ---------------------------------------------------------------- + +FCXForm::FCXForm(U32 _hasAdd, U32 _hasMult, S32 _redMultTerm, S32 _greenMultTerm, S32 _blueMultTerm, S32 _redAddTerm, S32 _greenAddTerm, S32 _blueAddTerm) +{ + hasAdd = _hasAdd; + hasMult = _hasMult; + redMultTerm = _redMultTerm; + greenMultTerm = _greenMultTerm; + blueMultTerm = _blueMultTerm; + redAddTerm = _redAddTerm; + greenAddTerm = _greenAddTerm; + blueAddTerm = _blueAddTerm; + nBits = MinBits(); +} + +// +U32 FCXForm::MinBits() +{ + + // two step process to find maximum value of 6 numbers because "FSWFStream::MaxNum" takes only 4 arguments + U32 maxValue = FSWFStream::MaxNum(redMultTerm, greenMultTerm, blueMultTerm, redAddTerm); + maxValue = FSWFStream::MaxNum(greenAddTerm, blueAddTerm, (S32)maxValue, 0); + + return FSWFStream::MinBits(maxValue, 1) + 1; +} + +// +void FCXForm::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->WriteBits(hasMult, 1); + _SWFStream->WriteBits(hasAdd, 1); + _SWFStream->WriteBits(nBits, 4); + + if (hasMult) { + _SWFStream->WriteBits((S32)redMultTerm, nBits); + _SWFStream->WriteBits((S32)greenMultTerm, nBits); + _SWFStream->WriteBits((S32)blueMultTerm, nBits); + } + if (hasAdd) { + _SWFStream->WriteBits((S32)redAddTerm, nBits); + _SWFStream->WriteBits((S32)greenAddTerm, nBits); + _SWFStream->WriteBits((S32)blueAddTerm, nBits); + } + + _SWFStream->FlushBits(); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FCXFormWAlpha ---------------------------------------------------------- + +FCXFormWAlpha::FCXFormWAlpha(U32 _hasAdd, U32 _hasMult, S32 _redMultTerm, S32 _greenMultTerm, S32 _blueMultTerm, S32 _alphaMultTerm, + S32 _redAddTerm, S32 _greenAddTerm, S32 _blueAddTerm, S32 _alphaAddTerm) + : FCXForm(_hasAdd, _hasMult, _redMultTerm, _greenMultTerm, _blueMultTerm, + _redAddTerm, _greenAddTerm, _blueAddTerm) +{ + alphaMultTerm = _alphaMultTerm; + + alphaAddTerm = _alphaAddTerm; + + nBits = MinBits(); +} + +U32 FCXFormWAlpha::MinBits() +{ + + // FFileWrite's MaxNum method takes only 4 arguments, so finding maximum value of 8 arguments takes three steps: + U32 maxMult = FSWFStream::MaxNum(redMultTerm, greenMultTerm, blueMultTerm, alphaMultTerm); + U32 maxAdd = FSWFStream::MaxNum(redAddTerm, greenAddTerm, blueAddTerm, alphaAddTerm); + U32 maxValue = FSWFStream::MaxNum((S32)maxMult, (S32)maxAdd, 0, 0); + + return FSWFStream::MinBits(maxValue, 1) + 1; +} + +void FCXFormWAlpha::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //fill? I think so + _SWFStream->FlushBits(); + + _SWFStream->WriteBits(hasMult, 1); + _SWFStream->WriteBits(hasAdd, 1); + _SWFStream->WriteBits(nBits, 4); + + if (hasMult) { + _SWFStream->WriteBits((S32)redMultTerm, nBits); + _SWFStream->WriteBits((S32)greenMultTerm, nBits); + _SWFStream->WriteBits((S32)blueMultTerm, nBits); + _SWFStream->WriteBits((S32)alphaMultTerm, nBits); + } + if (hasAdd) { + _SWFStream->WriteBits((S32)redAddTerm, nBits); + _SWFStream->WriteBits((S32)greenAddTerm, nBits); + _SWFStream->WriteBits((S32)blueAddTerm, nBits); + _SWFStream->WriteBits((S32)alphaAddTerm, nBits); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineMorphShape ---------------------------------------------------- + +FDTDefineMorphShape::FDTDefineMorphShape(FRect *_rect1, FRect *_rect2) +{ + + characterID = FObjCollection::Increment(); + shapeBounds_1 = _rect1; + shapeBounds_2 = _rect2; + morphFillStyleArray = new FFillStyleArray(); + morphLineStyleArray = new FLineStyleArray(); + styleArraysFinished = false; +} + +FDTDefineMorphShape::~FDTDefineMorphShape() +{ + + delete shapeBounds_1; + delete shapeBounds_2; + delete morphFillStyleArray; + delete morphLineStyleArray; +} + +U32 FDTDefineMorphShape::AddFillStyle(FMorphFillStyle *fillStyle) +{ + + assert(!styleArraysFinished); + return morphFillStyleArray->Add(fillStyle); +} + +U32 FDTDefineMorphShape::AddLineStyle(U32 startLineWidth, FColor *startLineColor, + U32 finalLineWidth, FColor *finalLineColor) +{ + assert(!styleArraysFinished); + + // here changed. + // morph shape always has RGBA color. + startLineColor->AlphaChannel(true); + finalLineColor->AlphaChannel(true); + + // Create a morph line style (one which contains both "morph from" and "morph to" line style information + FMorphLineStyle *startToFinalLine = new FMorphLineStyle(startLineWidth, finalLineWidth, + startLineColor, finalLineColor); + return morphLineStyleArray->Add(startToFinalLine); +} + +void FDTDefineMorphShape::FinishStyleArrays(void) +{ + + styleArraysFinished = true; + nFillBits = FSWFStream::MinBits(morphFillStyleArray->Size(), 0); + nLineBits = FSWFStream::MinBits(morphLineStyleArray->Size(), 0); + + shape1.SetFillBits(nFillBits); + shape1.SetLineBits(nLineBits); + + // asumption: shape2 will never contain fill or line syle info and this means no need + // for fill or line style bits +} + +void FDTDefineMorphShape::AddShapeRec_1(FShapeRec *_shapeRec) +{ + + assert(styleArraysFinished); + shape1.AddShapeRec(_shapeRec); +} + +void FDTDefineMorphShape::AddEdgeRec_2(FShapeRec *_shapeRec) +{ + + assert(styleArraysFinished); + _shapeRec->IncludeNFillBitInfo(0); // Always Zero + _shapeRec->IncludeNLineBitInfo(0); + shape2.AddShapeRec(_shapeRec); +} + +U16 FDTDefineMorphShape::ID(void) +{ + + return characterID; +} + +void FDTDefineMorphShape::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + + shapeBounds_1->WriteToSWFStream(&body); + + shapeBounds_2->WriteToSWFStream(&body); + + FSWFStream temp; + + morphFillStyleArray->WriteToSWFStream(&temp); + morphLineStyleArray->WriteToSWFStream(&temp); + + shape1.WriteToSWFStream(&temp); + + body.WriteDWord(temp.Size()); + + body.Append(&temp); + + shape2.WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagDefineMorphShape, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineShape --------------------------------------------------------- + +FDTDefineShape::FDTDefineShape(FRect *_rect) +{ + + characterID = FObjCollection::Increment(); + shapeBounds = _rect; + fillStyleArray = new FFillStyleArray(); + lineStyleArray = new FLineStyleArray(); + shapeWithStyle = NULL; + styleArraysFinished = false; +} + +FDTDefineShape::~FDTDefineShape() +{ + + delete fillStyleArray; + delete lineStyleArray; + delete shapeBounds; + delete shapeWithStyle; +} + +void FDTDefineShape::AddShapeRec(FShapeRec *_shapeRec) +{ + + //you must be done creating the fill style and line style arrays + //before you can begin adding shape records + assert(styleArraysFinished); + + //Change rec doesn't know how many bits for saving the FillStyle0 + //and FillStyle1. It gets this from the ShapeWithStyle which contains + //it. What happens when the change Rec also contains Fill and Line + //Syles??? The other types of ShapeRecs just use the "current style" + //and don't need this info so their methods don't actually do anything. + //Why not just go to the container when writing? + _shapeRec->IncludeNFillBitInfo(shapeWithStyle->NumFillBits()); + _shapeRec->IncludeNLineBitInfo(shapeWithStyle->NumLineBits()); + //now have the shapeWithStyle add the ShapeRec + shapeWithStyle->AddShapeRec(_shapeRec); +} + +//This shapes internal fillStyleArray adds a fillStyle to itself. +U32 FDTDefineShape::AddFillStyle(FFillStyle *fillStyle) +{ + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} +// here changed. +U32 FDTDefineShape::AddSolidFillStyle(FColor *fillColor) +{ + fillColor->AlphaChannel(false); + FFillStyle *fillStyle = new FFillStyleSolid(fillColor); + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} + +U32 FDTDefineShape::AddLineStyle(U32 lineWidth, FColor *lineColor) +{ + + assert(!styleArraysFinished); //once complete, cannot change + + // here changed. + lineColor->AlphaChannel(false); + FLineStyle *lineStyle = new FLineStyle(lineWidth, lineColor); + + //add line style to rectangle, remembering to store the position of the line style + //just as in the fill style + return lineStyleArray->Add(lineStyle); +} + +/*! Creates the shape with style. Can only do this once the fill style + and line style arrays are complete because the shapeWithStyle constructor + takes in complete fill and line style arrays. +*/ +void FDTDefineShape::FinishStyleArrays(void) +{ + + styleArraysFinished = true; + shapeWithStyle = new FShapeWStyle(fillStyleArray, lineStyleArray); + fillStyleArray = NULL; /*AMM*/ + lineStyleArray = NULL; +} + +U16 FDTDefineShape::ID(void) +{ + + return characterID; +} + +//Called by the FObjCollection when writing to the stream +//In turn, it calls the WriteToSWFStream methods of its embedded objects. + +void FDTDefineShape::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream tempBuffer; + + tempBuffer.WriteWord((U32)characterID); + shapeBounds->WriteToSWFStream(&tempBuffer); + shapeWithStyle->WriteToSWFStream(&tempBuffer); + + _SWFStream->AppendTag(stagDefineShape, tempBuffer.Size(), &tempBuffer); +} + +bool FDTDefineShape::IsDefineShape() +{ + return true; +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineShape2 -------------------------------------------------------- +FDTDefineShape2::FDTDefineShape2(FRect *_rect) +{ + + characterID = FObjCollection::Increment(); + shapeBounds = _rect; + fillStyleArray = new FFillStyleArray(); + lineStyleArray = new FLineStyleArray(); + shapeWithStyle = NULL; + styleArraysFinished = false; +} + +FDTDefineShape2::~FDTDefineShape2() +{ + + delete fillStyleArray; + delete lineStyleArray; + delete shapeBounds; + delete shapeWithStyle; +} + +void FDTDefineShape2::AddShapeRec(FShapeRec *_shapeRec) +{ + + //you must be done creating the fill style and line style arrays + //before you can begin adding shape records + assert(styleArraysFinished); + + _shapeRec->IncludeNFillBitInfo(shapeWithStyle->NumFillBits()); + _shapeRec->IncludeNLineBitInfo(shapeWithStyle->NumLineBits()); + shapeWithStyle->AddShapeRec(_shapeRec); +} + +U32 FDTDefineShape2::AddFillStyle(FFillStyle *fillStyle) +{ + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} +// here changed. +U32 FDTDefineShape2::AddSolidFillStyle(FColor *fillColor) +{ + fillColor->AlphaChannel(false); + FFillStyle *fillStyle = new FFillStyleSolid(fillColor); + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} + +U32 FDTDefineShape2::AddLineStyle(U32 lineWidth, FColor *lineColor) +{ + + assert(!styleArraysFinished); //once complete, cannot change + + // here changed. + lineColor->AlphaChannel(false); + FLineStyle *lineStyle = new FLineStyle(lineWidth, lineColor); + + //add line style to rectangle, remembering to store the position of the line style + //just as in the fill style + return lineStyleArray->Add(lineStyle); +} + +// Creates the shape with style. Can only do this once the fill style +// and line style arrays are complete because the shapeWithStyle constructor +// takes in complete fill and line style arrays. + +void FDTDefineShape2::FinishStyleArrays(void) +{ + + styleArraysFinished = true; + shapeWithStyle = new FShapeWStyle(fillStyleArray, lineStyleArray); + fillStyleArray = NULL; /*AMM*/ + lineStyleArray = NULL; +} + +U16 FDTDefineShape2::ID(void) +{ + + return characterID; +} + +bool FDTDefineShape2::IsDefineShape() +{ + assert(false); + return true; +} + +void FDTDefineShape2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + shapeBounds->WriteToSWFStream(&body); + shapeWithStyle->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagDefineShape2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineShape3 -------------------------------------------------------- + +FDTDefineShape3::FDTDefineShape3(FRect *_rect) +{ + characterID = FObjCollection::Increment(); + shapeBounds = _rect; + fillStyleArray = new FFillStyleArray(); + lineStyleArray = new FLineStyleArray(); + shapeWithStyle = NULL; + styleArraysFinished = false; +} + +////////////////////////////////////////////////////////////////////////////////////// + +FDTDefineShape3::FDTDefineShape3() +{ + + characterID = FObjCollection::Increment(); + shapeBounds = 0; + fillStyleArray = new FFillStyleArray(); + lineStyleArray = new FLineStyleArray(); + shapeWithStyle = NULL; + styleArraysFinished = false; +} + +FDTDefineShape3::~FDTDefineShape3() +{ + + delete fillStyleArray; + delete lineStyleArray; + delete shapeBounds; + delete shapeWithStyle; +} + +void FDTDefineShape3::setBounds(FRect *_rect) +{ + if (shapeBounds) + delete shapeBounds; + shapeBounds = _rect; +} + +////////////////////////////////////////////////////////////////////////////////////// + +bool FDTDefineShape3::IsDefineShape() +{ + return true; +} + +////////////////////////////////////////////////////////////////////////////////////// + +inline FColor tpixel2fcolor(const TPixel &color) +{ + return FColor(color.r, color.g, color.b, color.m); +} + +////////////////////////////////////////////////////////////////////////////////////// + +inline TPixel fcolor2tpixel(FColor &color) +{ + return TPixel(color.Red(), color.Green(), color.Blue(), color.Alpha()); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void FDTDefineShape3::changeColor(const std::map &table) +{ + unsigned int i; + if (fillStyleArray) + for (i = 0; i < fillStyleArray->Size(); i++) { + FAFillStyle *style = fillStyleArray->get(i); + if (style->IsSolidStyle()) { + TPixel oldColor = fcolor2tpixel(*(((FFillStyleSolid *)style)->getColor())); + std::map::const_iterator it = table.find(oldColor); + if (it != table.end()) + ((FFillStyleSolid *)style)->setColor(new FColor(tpixel2fcolor(it->second))); + } + } + + shapeWithStyle->changeColor(table); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void FDTDefineShape3::changeColor(const FColor &oldColor, const FColor &newColor) +{ + unsigned int i; + if (fillStyleArray) + for (i = 0; i < fillStyleArray->Size(); i++) { + FAFillStyle *style = fillStyleArray->get(i); + if (style->IsSolidStyle() && (*((FFillStyleSolid *)style)->getColor()) == oldColor) + ((FFillStyleSolid *)style)->setColor(new FColor(newColor)); + } + + shapeWithStyle->changeColor(oldColor, newColor); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void FDTDefineShape3::AddShapeRec(FShapeRec *_shapeRec) +{ + + //you must be done creating the fill style and line style arrays + //before you can begin adding shape records + assert(styleArraysFinished); + + _shapeRec->IncludeNFillBitInfo(shapeWithStyle->NumFillBits()); + _shapeRec->IncludeNLineBitInfo(shapeWithStyle->NumLineBits()); + shapeWithStyle->AddShapeRec(_shapeRec); +} + +U32 FDTDefineShape3::AddFillStyle(FFillStyle *fillStyle) +{ + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} + +// here changed. +U32 FDTDefineShape3::AddSolidFillStyle(FColor *fillColor) +{ + fillColor->AlphaChannel(true); + FFillStyle *fillStyle = new FFillStyleSolid(fillColor); + + assert(!styleArraysFinished); //once complete, cannot change + return fillStyleArray->Add(fillStyle); +} + +U32 FDTDefineShape3::AddLineStyle(U32 lineWidth, FColor *lineColor) +{ + + assert(!styleArraysFinished); //once complete, cannot change + + // here changed. + lineColor->AlphaChannel(true); + FLineStyle *lineStyle = new FLineStyle(lineWidth, lineColor); + + //add line style to rectangle, remembering to store the position of the line style + //just as in the fill style + return lineStyleArray->Add(lineStyle); +} + +// Creates the shape with style. Can only do this once the fill style +// and line style arrays are complete because the shapeWithStyle constructor +// takes in complete fill and line style arrays. + +void FDTDefineShape3::FinishStyleArrays(void) +{ + + styleArraysFinished = true; + shapeWithStyle = new FShapeWStyle(fillStyleArray, lineStyleArray); + fillStyleArray = NULL; + lineStyleArray = NULL; +} + +U16 FDTDefineShape3::ID(void) +{ + + return characterID; +} + +void FDTDefineShape3::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteWord((U32)characterID); + shapeBounds->WriteToSWFStream(&body); + shapeWithStyle->WriteToSWFStream(&body); + + _SWFStream->AppendTag(stagDefineShape3, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FFillStyleArray -------------------------------------------------------- + +FFillStyleArray::~FFillStyleArray() +{ + while (!fillStyleArray.empty()) { + delete fillStyleArray.back(); + fillStyleArray.pop_back(); + } +} + +// Returns the size of the fill style list. +U32 FFillStyleArray::Size(void) +{ + return (U32)fillStyleArray.size(); +} + +// The given fill style is added to the end of the fill style array. The position of +// the added fill style is returned so that the fill style can later be referenced. + +U32 FFillStyleArray::Add(FAFillStyle *fillStyle) +{ + FLASHASSERT(fillStyle); + + fillStyleArray.push_back(fillStyle); + return ((U32)fillStyleArray.size()); +} + +////////////////////////////////////////////////////////////////////////////////////// + +FAFillStyle *FFillStyleArray::get(unsigned int index) +{ + return fillStyleArray[index]; +} + +// Writes to the stream by travelling through all of the nodes in the array and writing +// their fill styles. First has to write the count of fill style arrays. See's if count +// is small enough to fit in to an 8 bit field, and either writes the count into an 8 bit +// field, or writes all 1's into an 8 bit field and writes the real count into a 16 bit +// field. + +void FFillStyleArray::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the size + U32 size = fillStyleArray.size(); + + if (size >= 0xff) { + + _SWFStream->WriteByte(0xff); + _SWFStream->WriteWord(size); + + } else { + + _SWFStream->WriteByte(size); + } + + //write the fill styles + std::vector::iterator cursor; + + for (cursor = fillStyleArray.begin(); cursor != fillStyleArray.end(); cursor++) { + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FFillStyleBitmap ------------------------------------------------------- + +// The tiledFlag indicates if the Bitmap fill style is tiled (tiledFlag==1) or +// clipped (tiledFlag==0). +FFillStyleBitmap::FFillStyleBitmap(int tiled, U16 ID, FMatrix *matrix) +{ + + tiledFlag = tiled; + bitmapID = ID; + bitmapMatrix = matrix; +} + +// Deletes the matrix. + +FFillStyleBitmap::~FFillStyleBitmap(void) +{ + + delete bitmapMatrix; + bitmapMatrix = NULL; +} + +// Writes the bitmap fill style to the given FSWFStream. + +void FFillStyleBitmap::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the type + if (tiledFlag) + _SWFStream->WriteByte(fillTiledBits); + else + _SWFStream->WriteByte(fillClippedBits); + + //write the bitmap id + _SWFStream->WriteWord(bitmapID); + + //write the matrix + bitmapMatrix->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FFillStyleGradient ----------------------------------------------------- + +// The linearFlag indicates if the gradient fill style is linear (linearFlag==1) or +// radial (linearFlag==0). +FFillStyleGradient::FFillStyleGradient(int linear, FMatrix *matrix, FGradient *gradient) +{ + linearFlag = linear; + gradFillMatrix = matrix; + gradFill = gradient; +} + +// Deletes the matrix and gradient. + +FFillStyleGradient::~FFillStyleGradient(void) +{ + delete gradFillMatrix; + delete gradFill; +} + +// Writes the Gradient fill style to the given FSWFStream. + +void FFillStyleGradient::WriteToSWFStream(FSWFStream *_SWFStream) +{ + //write the type + if (linearFlag) + _SWFStream->WriteByte(fillLinearGradient); + else + _SWFStream->WriteByte(fillRadialGradient); + + //write the matrix + gradFillMatrix->WriteToSWFStream(_SWFStream); + + //write the gradient + gradFill->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FFillStyleSolid -------------------------------------------------------- + +// Stores the color of the solid fill style. +FFillStyleSolid::FFillStyleSolid(FColor *_color) : color(_color) {} + +FFillStyleSolid::~FFillStyleSolid() +{ + delete color; +} + +// Writes the solid fill style to the given FSWFStream. + +void FFillStyleSolid::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the type + _SWFStream->WriteByte(fillSolid); //cast to U32? + + //write the color + color->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FGradient -------------------------------------------------------------- + +// Constructor. The grad record list is automatically constructed. +FGradient::FGradient(void) {} + +// Removes and deletes all the grad records from the grad record list. + +FGradient::~FGradient(void) +{ + + while (!gradRecords.empty()) { + + delete gradRecords.front(); + + gradRecords.pop_front(); + } +} + +// Adds the given grad record to the end of the grad record list. + +void FGradient::Add(FAGradRecord *gradRec) +{ + + gradRecords.push_back(gradRec); +} + +// Writes the grad records to the given _SWFStream. + +void FGradient::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the size + + _SWFStream->WriteByte((U32)gradRecords.size()); + + //write the grad records + + std::list::iterator cursor; + + for (cursor = gradRecords.begin(); cursor != gradRecords.end(); cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FGradientRecord -------------------------------------------------------- + +// FGradRecord class constructor. +FGradRecord::FGradRecord(U32 _ratio, FColor *_color) +{ + color = _color; + ratio = _ratio; +} + +FGradRecord::~FGradRecord() +{ + delete color; +} + +// Writes the grad record to the given buffer. + +void FGradRecord::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->WriteByte((U32)ratio); + + color->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FLineStyle ------------------------------------------------------------- + +// LineStyle class constructor. +FLineStyle::FLineStyle(U32 _width, FColor *_color) +{ + color = _color; + width = _width; //in TWIPS +} + +FLineStyle::~FLineStyle() +{ + delete color; +} + +// Writes the object to the given _SWFStream. + +void FLineStyle::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->WriteWord(width); + color->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FLineStyleArray -------------------------------------------------------- + +// The line style array is automatically constructed +FLineStyleArray::FLineStyleArray(void) {} + +// Removes and deletes every element in the list. + +FLineStyleArray::~FLineStyleArray(void) +{ + + while (!lineStyleArray.empty()) { + + delete lineStyleArray.front(); + lineStyleArray.pop_front(); + } +} + +// Returns the size of the line style list. + +U32 FLineStyleArray::Size(void) +{ + + return (U32)lineStyleArray.size(); +} + +// The given line style is added to the end of the line style array. The position of +// the added line style is returned so that the line style can later be referenced. + +U32 FLineStyleArray::Add(FALineStyle *lineStyle) +{ + + lineStyleArray.push_back(lineStyle); + return ((U32)lineStyleArray.size()); +} + +// Writes to the stream by travelling through all of the nodes in the array and writing +// their line styles. First has to write the count of line style arrays. See's if count +// is small enough to fit in to an 8 bit field, and either writes the count into an 8 bit +// field, or writes all 1's into an 8 bit field and writes the real count into a 16 bit +// field. + +void FLineStyleArray::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the size + U32 size = lineStyleArray.size(); + + if (size >= 0xff) { + + _SWFStream->WriteByte(0xff); + _SWFStream->WriteWord(size); + + } else { + + _SWFStream->WriteByte(size); + } + + //write the line styles + std::list::iterator cursor; + + for (cursor = lineStyleArray.begin(); cursor != lineStyleArray.end(); cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMorphFillStyleBitmap -------------------------------------------------- + +// The tiledFlag indicates if the Bitmap fill style is tiled (tiledFlag==1) or +// clipped (tiledFlag==0). +FMorphFillStyleBitmap::FMorphFillStyleBitmap(int tiled, U16 ID, + FMatrix *matrix1, FMatrix *matrix2) +{ + + tiledFlag = tiled; + bitmapID = ID; + bitmapMatrix1 = matrix1; + bitmapMatrix2 = matrix2; +} + +// Deletes the matrices. + +FMorphFillStyleBitmap::~FMorphFillStyleBitmap(void) +{ + + delete bitmapMatrix1; + bitmapMatrix1 = NULL; + + delete bitmapMatrix2; + bitmapMatrix2 = NULL; +} + +// Writes the bitmap fill style to the given FSWFStream. + +void FMorphFillStyleBitmap::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the type + if (tiledFlag) + _SWFStream->WriteByte(fillTiledBits); + else + _SWFStream->WriteByte(fillClippedBits); + + //write the bitmap id + _SWFStream->WriteWord(bitmapID); + + //write the matrices + bitmapMatrix1->WriteToSWFStream(_SWFStream); + bitmapMatrix2->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMorphFillStyleGradient ------------------------------------------------ + +// The linearFlag indicates if the gradient fill style is linear (linearFlag==1) or +// radial (linearFlag==0). +FMorphFillStyleGradient::FMorphFillStyleGradient(int linear, FMatrix *matrix1, FMatrix *matrix2, + FGradient *gradient) +{ + + linearFlag = linear; + gradFillMatrix1 = matrix1; + gradFillMatrix2 = matrix2; + gradFill = gradient; +} + +// Deletes the matrices and gradient. + +FMorphFillStyleGradient::~FMorphFillStyleGradient(void) +{ + + delete gradFillMatrix1; + gradFillMatrix1 = NULL; + delete gradFillMatrix2; + gradFillMatrix2 = NULL; + delete gradFill; + gradFill = NULL; +} + +// Writes the Gradient fill style to the given FSWFStream. + +void FMorphFillStyleGradient::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the type + if (linearFlag) + _SWFStream->WriteByte(fillLinearGradient); + else + _SWFStream->WriteByte(fillRadialGradient); + + //write the matrices + gradFillMatrix1->WriteToSWFStream(_SWFStream); + gradFillMatrix2->WriteToSWFStream(_SWFStream); + + //write the gradient + gradFill->WriteToSWFStream(_SWFStream); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMorphFillStyleSolid --------------------------------------------------- + +// Stores the colors of the solid fill style. +FMorphFillStyleSolid::FMorphFillStyleSolid(FColor *_color1, FColor *_color2) +{ + color1 = _color1; + color2 = _color2; +} + +FMorphFillStyleSolid::~FMorphFillStyleSolid() +{ + delete color1; + delete color2; +} + +// Writes the solid fill style to the given FSWFStream. + +void FMorphFillStyleSolid::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //write the type + _SWFStream->WriteByte(fillSolid); //cast to U32? + + //write the colors + color1->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. + color2->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMorphGradRecord ------------------------------------------------------- + +// Constructor. +FMorphGradRecord::FMorphGradRecord(U32 _ratio1, FColor *_color1, U32 _ratio2, FColor *_color2) +{ + + ratio1 = _ratio1; + ratio2 = _ratio2; + + color1 = _color1; + color2 = _color2; +} + +FMorphGradRecord::~FMorphGradRecord() +{ + delete color1; + delete color2; +} + +// Writes to the given _SWFStream. + +void FMorphGradRecord::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->WriteByte((U32)ratio1); + color1->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. + + _SWFStream->WriteByte((U32)ratio2); + color2->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMorphLineStyle -------------------------------------------------------- + +//The line style used by morph shapes +FMorphLineStyle::FMorphLineStyle(U32 _width1, U32 _width2, FColor *_color1, + FColor *_color2) : color1(_color1), color2(_color2) +{ + width1 = _width1; + width2 = _width2; + + color1 = _color1; + color2 = _color2; +} + +FMorphLineStyle::~FMorphLineStyle() +{ + delete color1; + delete color2; +} + +// Writes to the given _SWFStream. + +void FMorphLineStyle::WriteToSWFStream(FSWFStream *_SWFStream) +{ + //write the widths + _SWFStream->WriteWord(width1); + _SWFStream->WriteWord(width2); + + //write the colors + color1->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. + color2->WriteToSWFStream(_SWFStream); //, FColor::WRITE_SMALLEST ); changed here. +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShape ----------------------------------------------------------------- + +// Shape class constructor. The shape record list is automatically constructed +FShape::FShape(void) +{ + + nFillBits = 0; + nLineBits = 0; +} + +// Removes and deletes every element in the list. + +FShape::~FShape(void) +{ + + while (!shapeRecs.empty()) { + + delete shapeRecs.back(); + shapeRecs.pop_back(); + } +} + +// Sets the nFillBits field. + +void FShape::SetFillBits(U32 _nFillBits) +{ + + nFillBits = _nFillBits; +} + +// Sets the nLineBits field. + +void FShape::SetLineBits(U32 _nLineBits) +{ + + nLineBits = _nLineBits; +} + +// Adds a shape record to the end of the shape record list. + +void FShape::AddShapeRec(FShapeRec *shapeRec) +{ + + shapeRecs.push_back(shapeRec); +} + +// Writes the object to the given buffer. + +void FShape::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + _SWFStream->WriteBits(nFillBits, 4); + _SWFStream->WriteBits(nLineBits, 4); + + std::vector::iterator cursor; + + for (cursor = shapeRecs.begin(); cursor != shapeRecs.end(); cursor++) { + //the shape rec might be referring to a fillor line style arry + //external (ShapesWStyle) and will need the # of fillBits. + (*cursor)->IncludeNFillBitInfo(nFillBits); //enter fillbits + (*cursor)->IncludeNLineBitInfo(nLineBits); //enter linebits + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShapeRecChange -------------------------------------------------------- + +// FShapeRecChange class constructor. It is passed the nFillBits and nLineBits values. +FShapeRecChange::FShapeRecChange(U32 _stateNewStyles, + U32 _stateLineStyle, + U32 _stateFillStyle1, + U32 _stateFillStyle0, + U32 _stateMoveTo, + S32 _moveDeltaX, + S32 _moveDeltaY, + U32 _fill0Style, + U32 _fill1Style, + U32 _lineStyle, + FFillStyleArray *_fillStyles, + FLineStyleArray *_lineStyles) +{ + stateNewStyles = _stateNewStyles; + stateLineStyle = _stateLineStyle; + stateFillStyle1 = _stateFillStyle1; + stateFillStyle0 = _stateFillStyle0; + stateMoveTo = _stateMoveTo; + + moveDeltaX = _moveDeltaX; + moveDeltaY = _moveDeltaY; + fill0Style = _fill0Style; + fill1Style = _fill1Style; + lineStyle = _lineStyle; + fillStyles = _fillStyles; + lineStyles = _lineStyles; + nMoveBits = MinBits(); +} + +FFillStyleArray *FShapeRecChange::GetFillStyles() +{ + return fillStyles; +} + +// Deletes fillStyles and lineStyles if they exist. + +FShapeRecChange::~FShapeRecChange(void) +{ + + if (fillStyles) { //if fillStyles isn't NULL + + delete fillStyles; + fillStyles = NULL; + } + + if (lineStyles) { //if lineStyles isn't NULL + + delete lineStyles; + lineStyles = NULL; + } +} + +//!Change Rec needs to know how many bits to write for the Fill and Line Styles +/*!Change Rec doesn't actually store the nLineBits or nFillBits, but needs to know + them when it writes the active style fields so that it knows how many bits + to write + \param _nFillBits the size in bits of the index into the FillStyleArray +*/ +void FShapeRecChange::IncludeNFillBitInfo(U32 _nFillBits) +{ + + nFillBits = _nFillBits; +} + +//!Change Rec needs to know how many bits to write for the Fill and Line Styles +/*!Change Rec doesn't actually store the nLineBits or nFillBits, but needs to know + them when it writes the active style fields so that it knows how many bits + to write + \param _nLineBits the size in bits of the index into the LineStyleArray +*/ +void FShapeRecChange::IncludeNLineBitInfo(U32 _nLineBits) +{ + + nLineBits = _nLineBits; +} + +// Calculates nMoveBits by returning the number of bits needed to store the larger of +// moveDeltaX and moveDeltaY. + +U32 FShapeRecChange::MinBits(void) +{ + + U32 max = FSWFStream::MaxNum(moveDeltaX, moveDeltaY, 0, 0); + return FSWFStream::MinBits(max, 1); +} + +// Writes the shape record to the given _SWFStream. + +void FShapeRecChange::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //non-edge record flag + _SWFStream->WriteBits(NOT_EDGE_REC, 1); + + _SWFStream->WriteBits(stateNewStyles, 1); + _SWFStream->WriteBits(stateLineStyle, 1); + _SWFStream->WriteBits(stateFillStyle1, 1); + _SWFStream->WriteBits(stateFillStyle0, 1); + _SWFStream->WriteBits(stateMoveTo, 1); + + if (stateMoveTo) { + + _SWFStream->WriteBits(nMoveBits, 5); + _SWFStream->WriteBits(moveDeltaX, nMoveBits); + _SWFStream->WriteBits(moveDeltaY, nMoveBits); + } + + if (stateFillStyle0) { + + _SWFStream->WriteBits(fill0Style, nFillBits); + } + + if (stateFillStyle1) { + + _SWFStream->WriteBits(fill1Style, nFillBits); + } + + if (stateLineStyle) { + + _SWFStream->WriteBits(lineStyle, nLineBits); + } + + if (stateNewStyles) { + + fillStyles->WriteToSWFStream(_SWFStream); + lineStyles->WriteToSWFStream(_SWFStream); + + nFillBits = FSWFStream::MinBits(fillStyles->Size(), 0); + nLineBits = FSWFStream::MinBits(lineStyles->Size(), 0); + + _SWFStream->WriteBits(nFillBits, 4); //li mortacci loro!!! "dimenticavano" di mettere questi... + _SWFStream->WriteBits(nLineBits, 4); + } + + //NO FILLING IN BETWEEN SHAPE RECORDS +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShapeRecEdgeStraight -------------------------------------------------- + +FShapeRecEdgeStraight::FShapeRecEdgeStraight(S32 dx, S32 dy) +{ + generalLineFlag = 0; + generalDeltaX = 0; + generalDeltaY = 0; + verticalLineFlag = 0; + horizontalDeltaX = 0; + verticalDeltaY = 0; + + edgeFlag = 1; + + // is this a general line? + if (dx != 0 && dy != 0) { + generalLineFlag = true; + generalDeltaX = dx; + generalDeltaY = dy; + } else if (dx == 0) // not general, is it vertical? + { + verticalLineFlag = true; + verticalDeltaY = dy; + } else { + verticalLineFlag = false; + horizontalDeltaX = dx; + } + nBits = MinBits(); + assert(nBits < 16 + 2); //vincenzo: se no non entra nei quattro bits riservati per scrivere questo valore... +} + +U32 FShapeRecEdgeStraight::MinBits(void) +{ + + U32 maxDelta = FSWFStream::MaxNum(generalDeltaX, generalDeltaY, + horizontalDeltaX, verticalDeltaY); + + return FSWFStream::MinBits(maxDelta, 1); +} + +void FShapeRecEdgeStraight::IncludeNFillBitInfo(U32 /*_nFillBits*/) +{ +} + +void FShapeRecEdgeStraight::IncludeNLineBitInfo(U32 /*_nLineBits*/) +{ +} + +void FShapeRecEdgeStraight::WriteToSWFStream(FSWFStream *_SWFStream) +{ + //edge record flag + _SWFStream->WriteBits(EDGE_REC, 1); + + _SWFStream->WriteBits(STRAIGHT_EDGE, 1); //This is a Straight edge record + _SWFStream->WriteBits(nBits - 2, 4); + _SWFStream->WriteBits(generalLineFlag, 1); + + if (generalLineFlag) { + + _SWFStream->WriteBits((U32)generalDeltaX, nBits); + _SWFStream->WriteBits((U32)generalDeltaY, nBits); + + } else { + + _SWFStream->WriteBits(verticalLineFlag, 1); //verticalFlag is supposed to be signed but don't think it matters + + if (!verticalLineFlag) + _SWFStream->WriteBits((U32)horizontalDeltaX, nBits); + + else + _SWFStream->WriteBits((U32)verticalDeltaY, nBits); + } + + //NO FILLING IN BETWEEN SHAPE RECORDS +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShapeRecEdgeCurved --------------------------------------------------- + +// FShapeRecEdge class constructor. +FShapeRecEdgeCurved::FShapeRecEdgeCurved(S32 controlDX, S32 controlDY, S32 anchorDX, S32 anchorDY) +{ + edgeFlag = 0; + + controlDeltaX = controlDX; + controlDeltaY = controlDY; + anchorDeltaX = anchorDX; + anchorDeltaY = anchorDY; + + nBits = MinBits(); +} +// Finds the min bits necessary to represent the 4 fields, by seeing how many bits are +// necessary to represent the largest field of the four. + +U32 FShapeRecEdgeCurved::MinBits(void) +{ + + U32 maxDelta = FSWFStream::MaxNum(controlDeltaX, controlDeltaY, anchorDeltaX, anchorDeltaY); + + return FSWFStream::MinBits(maxDelta, 1); +} + +void FShapeRecEdgeCurved::IncludeNFillBitInfo(U32 /*_nFillBits*/) +{ +} + +void FShapeRecEdgeCurved::IncludeNLineBitInfo(U32 /*_nLineBits*/) +{ +} + +// Writes the shape record to the given _SWFStream. + +void FShapeRecEdgeCurved::WriteToSWFStream(FSWFStream *_SWFStream) +{ + //edge record flag + _SWFStream->WriteBits(EDGE_REC, 1); + + _SWFStream->WriteBits(CURVED_EDGE, 1); //This is a curved edge record + + _SWFStream->WriteBits(nBits - 2, 4); + + _SWFStream->WriteBits((U32)controlDeltaX, nBits); + _SWFStream->WriteBits((U32)controlDeltaY, nBits); + _SWFStream->WriteBits((U32)anchorDeltaX, nBits); + _SWFStream->WriteBits((U32)anchorDeltaY, nBits); + + //NO FILLING IN BETWEEN SHAPE RECORDS +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShapeRecEnd ---------------------------------------------------------- + +// ShapeRecEnd class constructor. Doesn't take in anything because the +// object serves as an end tag and has no details. +FShapeRecEnd::FShapeRecEnd(void) {} + +void FShapeRecEnd::IncludeNFillBitInfo(U32 /*_nFillBits*/) +{ + // does nothing + //virtual method needed for shape rec change +} + +void FShapeRecEnd::IncludeNLineBitInfo(U32 /*_nLineBits*/) +{ + //same deal +} + +// Writes the object to the given buffer. + +void FShapeRecEnd::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + //stream of 0's signifies the end + _SWFStream->WriteBits(0, 6); + + //need to fill to end shape rec array structure. + _SWFStream->FlushBits(); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FShapeWStyle ---------------------------------------------------------- + +// FShapeWStyle class constructor. The shape record list is automatically constructed. +FShapeWStyle::FShapeWStyle(FFillStyleArray *_fillStyles, FLineStyleArray *_lineStyles) +{ + + fillStyles = _fillStyles; + lineStyles = _lineStyles; + + nFillBits = FSWFStream::MinBits(fillStyles->Size(), 0); + nLineBits = FSWFStream::MinBits(lineStyles->Size(), 0); +} + +// Deleted the fill style array, line style array, and shape records. + +FShapeWStyle::~FShapeWStyle(void) +{ + + delete fillStyles; + fillStyles = NULL; + + delete lineStyles; + lineStyles = NULL; + + while (!shapeRecs.empty()) { + + delete shapeRecs.back(); + shapeRecs.pop_back(); + } +} + +// Adds a shape record to the end of the shape record list. + +void FShapeWStyle::AddShapeRec(FShapeRec *shapeRec) +{ + + shapeRecs.push_back(shapeRec); +} + +U32 FShapeWStyle::NumFillBits() +{ + + return nFillBits; +} + +U32 FShapeWStyle::NumLineBits() +{ + + return nLineBits; +} + +////////////////////////////////////////////////////////////////////// + +void FShapeWStyle::changeColor(const std::map &table) +{ + unsigned int i, j; + if (fillStyles) + for (i = 0; i < fillStyles->Size(); i++) { + FAFillStyle *style = fillStyles->get(i); + //FColor* color = ((FFillStyleSolid*)style)->getColor(); + if (style->IsSolidStyle()) { + TPixel oldColor = fcolor2tpixel(*(((FFillStyleSolid *)style)->getColor())); + std::map::const_iterator it = table.find(oldColor); + if (it != table.end()) + ((FFillStyleSolid *)style)->setColor(new FColor(tpixel2fcolor(it->second))); + } + } + + for (i = 0; i < shapeRecs.size(); i++) { + FShapeRec *rec = shapeRecs[i]; + if (rec->isFShapeRecChange() && ((FShapeRecChange *)rec)->GetFillStyles()) + for (j = 0; j < ((FShapeRecChange *)rec)->GetFillStyles()->Size(); j++) { + FAFillStyle *style = ((FShapeRecChange *)rec)->GetFillStyles()->get(j); + //FColor* color = ((FFillStyleSolid*)style)->getColor(); + if (style->IsSolidStyle()) { + TPixel oldColor = fcolor2tpixel(*(((FFillStyleSolid *)style)->getColor())); + std::map::const_iterator it = table.find(oldColor); + if (it != table.end()) + ((FFillStyleSolid *)style)->setColor(new FColor(tpixel2fcolor(it->second))); + } + } + } +} + +///////////////////////////////////////////////////////////////////// + +void FShapeWStyle::changeColor(const FColor &oldColor, const FColor &newColor) +{ + unsigned int i, j; + if (fillStyles) + for (i = 0; i < fillStyles->Size(); i++) { + FAFillStyle *style = fillStyles->get(i); + FColor *color = ((FFillStyleSolid *)style)->getColor(); + if (style->IsSolidStyle() && *color == oldColor) + ((FFillStyleSolid *)style)->setColor(new FColor(newColor)); + color = ((FFillStyleSolid *)style)->getColor(); + } + + for (i = 0; i < shapeRecs.size(); i++) { + FShapeRec *rec = shapeRecs[i]; + if (rec->isFShapeRecChange() && ((FShapeRecChange *)rec)->GetFillStyles()) + for (j = 0; j < ((FShapeRecChange *)rec)->GetFillStyles()->Size(); j++) { + FAFillStyle *style = ((FShapeRecChange *)rec)->GetFillStyles()->get(j); + FColor *color = ((FFillStyleSolid *)style)->getColor(); + if (style->IsSolidStyle() && *color == oldColor) + ((FFillStyleSolid *)style)->setColor(new FColor(newColor)); + color = ((FFillStyleSolid *)style)->getColor(); + } + } +} + +///////////////////////////////////////////////////////////////////// + +// Writes the shape with style to the given _SWFStream. + +void FShapeWStyle::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + fillStyles->WriteToSWFStream(_SWFStream); + lineStyles->WriteToSWFStream(_SWFStream); + + _SWFStream->WriteBits(nFillBits, 4); + _SWFStream->WriteBits(nLineBits, 4); + + //write the shape records + std::vector::iterator cursor; + + for (cursor = shapeRecs.begin(); cursor != shapeRecs.end(); cursor++) { + (*cursor)->IncludeNFillBitInfo(nFillBits); //vincenzo + (*cursor)->IncludeNLineBitInfo(nLineBits); //vincenzo + + (*cursor)->WriteToSWFStream(_SWFStream); + if ((*cursor)->isFShapeRecChange()) //vincenzo + ((FShapeRecChange *)(*cursor))->getFillLineBits(nFillBits, nLineBits); //vincenzo + } + _SWFStream->FlushBits(); +} diff --git a/toonz/sources/common/flash/FDTShapes.h b/toonz/sources/common/flash/FDTShapes.h new file mode 100644 index 0000000..488c62d --- /dev/null +++ b/toonz/sources/common/flash/FDTShapes.h @@ -0,0 +1,1278 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTShapes.h + + This header-file contains the declarations of all low-level shape-related classes. + Their parent classes are in the parentheses: + + class FShape; + class FACXForm; + class FALineStyle; + class FAGradRecord; + class FCXForm; (public FACXForm) + class FCXFormWAlpha; (public FACXForm) + class FDTDefineMorphShape; (public FDT) + class FDTDefineShape; (public FDT) + class FDTDefineShape2; (public FDT) + class FDTDefineShape3; (public FDT) + class FAFillStyle; + class FFillStyle; (public FAFillStyle) + class FFillStyleArray; + class FFillStyleBitmap; (public FFillStyle) + class FFillStyleGradient; (public FFillStyle) + class FFillStyleSolid; (public FFillStyle) + class FGradient; + class FGradRecord; (public FAGradRecord) + class FLineStyle; (public FALineStyle) + class FLineStyleArray; + class FMorphFillStyle; (public FAFillStyle) + class FMorphFillStyleBitmap; (public FMorphFillStyle) + class FMorphFillStyleGradient; (public FMorphFillStyle) + class FMorphFillStyleSolid; (public FMorphFillStyle) + class FMorphGradRecord; (public FAGradRecord) + class FMorphLineStyle; (public FALineStyle) + class FShapeRec; + class FShapeRecChange; (public FShapeRec) + class FShapeRecEdgeStraight; (public FShapeRec) + class FShapeRecEdgeCurved; (public FShapeRec) + class FShapeRecEnd; (public FShapeRec) + class FShapeWStyle; + +****************************************************************************************/ + +#ifndef _FDT_SHAPES_H_ +#define _FDT_SHAPES_H_ + +#include "Macromedia.h" +#include "FDT.h" +#include "FSWFStream.h" +#include "FPrimitive.h" +#include "tpixel.h" + +class FMorphFillStyle; +class FMorphLineStyle; +class FShapeRec; +class FFillStyle; +class FLineStyle; +class FFillStyleArray; +class FLineStyleArray; +class FShapeWStyle; +class FGradient; + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +//! Holds an array of ShapeRec's +/*! The ShapeRecs are written to a SWFStream in + the order they are added to the array. Normally used to store Font + Glyphs in a Definefont tag. An array of ShapeRec'sThis should probably + be called FShapeArray or such. FShapeWStyle is an expanded version. + \sa FShapeRec, FShapeRecChange, +*/ +class DVAPI FShape +{ +public: + FShape(); + virtual ~FShape(); + + //! Sets the number of bits necessary to index a Fill Style array into the SWF field nFillBits. + /*! The SWF SHAPE tag needs the number of bits necessary to index a + fillstyle array However, what Fill Style Array is it referring to ??? + Put 0 for now. + \param _nFillBits + */ + void SetFillBits(U32 _nFillBits); + //! Sets the number of bits necessary to index a Line Style array into theSWF field nFillBits. + /*! The SWF SHAPE tag also needs the number of bits necessary to index a + linestyle array. Again, what Line Style Array is it referring to ??? + Put 0 for now. + \param _nLineBits + */ + void SetLineBits(U32 _nLineBits); + + //! Add a ShapeRec (of type curve, line, or ChangeRec) to this Shape + /*! Add a ShapeRec which could be a ChangeRec, or an EdgeRec (curve or line) + to this Shape. The first is always a ChangeRec to set drawing position. + + \param shapeRec + */ + void AddShapeRec(FShapeRec *shapeRec); + + //! Stream the ShapeRecs out to the temporary buffer + /*! For each ShapeRec, tell it the # of fill bits, line bits, + and tell it to write itself to the temp stream. + + \param _SWFStream: the temporary output stream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); //not Complete, see comments + +private: + U32 nFillBits; + U32 nLineBits; + std::vector shapeRecs; +}; + +//! Abstract Base class of FCXForm and FCXFormWAlpha +/*! Specifies a color transform for certain objects + + \sa FCXForm, FCXFormWAlpha + */ +class DVAPI FACXForm +{ +public: + //! Virtual method. Write the color transform out to stream. + /*! Implemented by FCXForm, FCXFormWAlpha + + \param _SWFStream + */ + virtual ~FACXForm() {} + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; +}; + +//! Abstract Base class of FLineStyle, FMorphLineStyle +/*! All line styles fall into this category (morph or regular) + + \sa FLineStyle, FMorphLineStyle + */ +class DVAPI FALineStyle +{ +public: + virtual ~FALineStyle() {} + //! Virtual Method for writing LineStyles out to a stream + /*! Virtual Method for writing LineStyles out to a stream + + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; +}; + +// + +//! Virtual Base Class of FGradRecord,FMorphGradRecord +/*! All grad records fall into this category + + \sa FGradRecord,FMorphGradRecord + */ +class DVAPI FAGradRecord +{ +public: + virtual ~FAGradRecord() {} + + //! Virtual Method for writing a Gradient out to a stream + + /*! + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; +}; + +// + +//! Color Transform +/*! Specifies a color transformation for some object without transparency information + + \sa + */ +class DVAPI FCXForm : public FACXForm +{ +public: + //! Set the values of the transform - no alpha channel + /*! Do we need the _has parameters? + Is 0 a valid value for each of these mult or add params? + + \param U32 _hasAdd: Has color addition values if equal to 1 + \param U32 _hasMult: Has color multipy values if equal to 1 + \param S32 _redMultTerm: Red multiply value + \param S32 _greenMultTerm: Green multiply value + \param S32 _blueMultiTerm: Blue multiply value + \param S32 _redAddTerm: Red addition value + \param S32 _greenAddTerm: Green addition value + \param S32 _blueAddTerm: Blue addition value + + */ + FCXForm( + U32 _hasAdd, U32 _hasMult, S32 _redMultTerm, S32 _greenMultTerm, + S32 _blueMultiTerm, S32 _redAddTerm, S32 _greenAddTerm, S32 _blueAddTerm); + + //! Write a SWF Color Transform without Alpha to Stream + /*! Writes the _has bits, and depending on their values, the multiply and add + color values. Computes the SWF nBits field from color values passed. + + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +protected: + //flags + U32 hasAdd; + U32 hasMult; + + // bits in each term field + U32 nBits; + + S32 redMultTerm; + S32 greenMultTerm; + S32 blueMultTerm; + S32 redAddTerm; + S32 greenAddTerm; + S32 blueAddTerm; + + // finds minimum number of bits needed to represent the largest term + U32 MinBits(void); +}; + +// Specifies a color transformation for some object with transparency information + +//! Set the values of the transform - same as FCXForm with alpha channel +/*! Do we need the _has parameters? + Is 0 a valid value for each of these mult or add params? + + \param U32 _hasAdd: Has color addition values if equal to 1 + \param U32 _hasMult: Has color multipy values if equal to 1 + \param S32 _redMultTerm: Red multiply value + \param S32 _greenMultTerm: Green multiply value + \param S32 _blueMultiTerm: Blue multiply value + \param S32 _alphaMultTerm: Alpha transparency multiply value + \param S32 _redAddTerm: Red addition value + \param S32 _greenAddTerm: Green addition value + \param S32 _blueAddTerm: Blue addition value + \param S32 _alphaAddTerm: Alpha transparency addition value + + */ +class DVAPI FCXFormWAlpha : public FCXForm +{ + +public: + FCXFormWAlpha(U32 _hasAdd, U32 _hasMult, S32 _redMultTerm, S32 _greenMultTerm, S32 _blueMultTerm, S32 _alphaMultTerm, + S32 _redAddTerm, S32 _greenAddTerm, S32 _blueAddTerm, S32 _alphaAddTerm); + //! Write a SWF Color Transform with Alpha to Stream + /*! Writes the _has bits, and depending on their values, the multiply and add + color values. Computes the SWF nBits field from color values passed. + + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + //flags + // U32 hasAdd; + // U32 hasMult; + + //bits in each term field + U32 nBits; + + // S32 redMultTerm; + // S32 greenMultTerm; + // S32 blueMultTerm; + S32 alphaMultTerm; + + // S32 redAddTerm; + // S32 greenAddTerm; + // S32 blueAddTerm; + S32 alphaAddTerm; + + //returns minimum number of bits needed to represent largest term value + U32 MinBits(void); +}; + +// + +//! a flash object which defines a morphing shape. It contains appearance information about an original shape and a shape being morphed into +/*! Note: this is the SWF info and should be rewritten. I would have expected that a + morph would take two character IDs instead of defining everything here. ??? + Defines the metamorphosis of one shape (Shape1) into another (Shape2). + The ShapeBounds1 specifies the boundaries of the original shape, + while ShapeBounds2 specifies the boundaries of the shape into which + Shape1 changes. The data specified in MorphFillStyles and MorphLineStyles + are for both shapes. For example, the fill style for Shape1 is specified, + then the corresponding fill style for Shape2 is specified. Edges1 + specifies the edges for Shape1 as well as the morph style data for + both shapes. Edges2 specifies the edges for Shape2, and contains no + style data. The number of edges specified in Edges1 must equal the + number of edges in Edges2. + + Should point to example code here. + */ + +class DVAPI FDTDefineMorphShape : public FDT +{ + +public: + //! Constructor just takes the before and after bounds RECTs + /*! + \param _rect1, _rect2 rects of before and after shapes + */ + FDTDefineMorphShape(FRect *_rect1, FRect *_rect2); + virtual ~FDTDefineMorphShape(); + //! Add Fill style to array and record its index + /*! You should have used FMorphFillStyles (solid, gradient, bitmap) + to create a morph fill style + \param fillStyle + */ + U32 AddFillStyle(FMorphFillStyle *fillStyle); + + // here changed. + //! Add line style to array and record its index. + /*! You need a pair of line styles (lineWidth + lineColor), + which contains both "morph from" and "morph to" line style information. + \param startLineWidth Line thickness of starting shape in twips. + \param startLineColor Line color of starting shape. (should be RGBA type). + \param finalLineWidth Line thickness of ending shape in twips. + \param finalLineColor Line color of ending shape. (should be RGBA type). + \return the position in the array. + */ + U32 AddLineStyle(U32 startLineWidth, FColor *startLineColor, + U32 finalLineWidth, FColor *finalLineColor); + + //! Cleans up the internal representation of the fill and line style arrays. + void FinishStyleArrays(void); + + //! Add a Shape Record to the first morph object definition + /*! The AddShapeRec_1 method takes style information for both shapes and edge + information for the "morph from" shape + \param _shapeRec the change, straight, curve record, or end record + */ + void AddShapeRec_1(FShapeRec *_shapeRec); + //! Add a Shape Record to the second morph object + /*! The AddEdgeRec_2 method takes edge information for just the "morph to" + \param _shapeRec the change, straight, curve record, or end record + */ + void AddEdgeRec_2(FShapeRec *_shapeRec); + + //! Record morphing objects's ID for later reference + U16 ID(void); + + //! Called by the FObj to write to the output stream + /*! \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + FRect *shapeBounds_1; // 1 subscript denotes original shape + FRect *shapeBounds_2; // and 2 subscript denotes shape being morphed to + FShape shape1; + FShape shape2; + U32 offset; + FFillStyleArray *morphFillStyleArray; + FLineStyleArray *morphLineStyleArray; + U32 nFillBits; + U32 nLineBits; + U8 styleArraysFinished; +}; + +//! Create a DefineShape tag +/*! Creates a Flash 1.0 Define Shape tag. You should use DefineShape3. + All the DefineShape tags only differ in the fill/line style arrays + and fill/line style records + + \sa FFillStyle, FLineStyle, FShapeRec, FShapeWStyle, + see also FExampleRectangle.cpp + */ +class DVAPI FDTDefineShape : public FDT +{ + +public: + //! Create the basic empty shell of a DefineShape tag + /*! Only the bounds rect is passed. Fills, Lines, and + Shapes must be added. + + \param _rect the bounds rectangle + */ + FDTDefineShape(FRect *_rect); + virtual ~FDTDefineShape(); + + //! Add a Shape record to the internal ShapeRec array + /*! Use FShapeRecChange, FShapeRecEdgeStraight, FShapeRecEdgeStraight + to create edges to add to this shape + \param _shapeRec + */ + void AddShapeRec(FShapeRec *_shapeRec); + + //! Cleans up the internal representation of the fill and line style arrays. + void FinishStyleArrays(void); + //! Add fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. + \param fillStyle + \return the position in the array. when creating the shape change record later. + */ + U32 AddFillStyle(FFillStyle *fillStyle); + + // changed here. + //! Add solid fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. + \param fillColor + \return the position in the array. when creating the shape change record later. + */ + U32 AddSolidFillStyle(FColor *fillColor); + + // here changed. + //! Add line style to array and record its index. + /*! Remember to store the position of the fill style. Use this + when creating the shape change record later, to indicate which style + to use for the next edges. + \param lineWidth Line thickness in twips. + \param lineColor Line color. (should be RGB type). + \return the position in the array. + */ + U32 AddLineStyle(U32 lineWidth, FColor *lineColor); + + //! Record objects's ID for when you do a PlaceObject + U16 ID(void); + //! Called by the FObj to write to the output stream + /*! \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + virtual void SetId(U16 id) { characterID = id; } + bool IsDefineShape(); + +private: + U16 characterID; + FRect *shapeBounds; + FShapeWStyle *shapeWithStyle; + FFillStyleArray *fillStyleArray; + FLineStyleArray *lineStyleArray; + //U32 nFillBits; + //U32 nLineBits; + U8 styleArraysFinished; +}; + +//! Create a DefineShape2 tag +/*! Creates a Flash 2.0 Define Shape tag. You should use DefineShape3. + All the DefineShape tags only differ in the fill/line style arrays + and fill/line style records + + \sa FFillStyle, FLineStyle, FShapeRec, FShapeWStyle, + see also FExampleRectangle.cpp + */ +class DVAPI FDTDefineShape2 : public FDT +{ + +public: + //! Create the basic empty shell of a DefineShape tag + /*! Only the bounds rect is passed. Fills, Lines, and + Shapes must be added. + + \param _rect the bounds rectangle + */ + FDTDefineShape2(FRect *_rect); + virtual ~FDTDefineShape2(); + //! Add a Shape record to the internal ShapeRec array + /*! Use FShapeRecChange, FShapeRecEdgeStraight, FShapeRecEdgeStraight + to create edges to add to this shape + \param _shapeRec + */ + void AddShapeRec(FShapeRec *_shapeRec); + + //! Cleans up the internal representation of the fill and line style arrays. + void FinishStyleArrays(void); + + //! Add fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. Remember, DefineShape2s don't support Alpha. + \param fillStyle + \return the position in the array. When creating the shape change record later. + */ + U32 AddFillStyle(FFillStyle *fillStyle); + + // here changed. + //! Add solid fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. Remember, DefineShape2s don't support Alpha. + \param fillColor + \return the position in the array. When creating the shape change record later. + */ + U32 AddSolidFillStyle(FColor *fillColor); + + // here changed. + //! Add line style to rectangle, + /*! Remember to store the position of the fill style. Use this + when creating the shape change record later, to indicate which style + to use for the next edges. + \param lineWidth Line thickness in twips. + \param lineColor Line color. (should be RGB type). + \return the position in the array. + */ + U32 AddLineStyle(U32 lineWidth, FColor *lineColor); + + //! Record objects's ID for when you do a PlaceObject + U16 ID(void); + //! Called by the FObj to write to the output stream + /*! \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + virtual void SetId(U16 id) { characterID = id; } + bool IsDefineShape(); + +private: + U16 characterID; + FRect *shapeBounds; + FShapeWStyle *shapeWithStyle; + FFillStyleArray *fillStyleArray; + FLineStyleArray *lineStyleArray; + U32 nFillBits; + U32 nLineBits; + U8 styleArraysFinished; +}; + +//! Create a DefineShape3 tag +/*! Creates a Flash 3.0 Define Shape tag. + All the DefineShape tags only differ in the fill/line style arrays + and fill/line style records (accepts alpha color values). + + \sa FFillStyle, FLineStyle, FShapeRec, FShapeWStyle, + see also FExampleRectangle.cpp + */ + +class DVAPI FDTDefineShape3 : public FDT +{ + +public: + //! Create the basic empty shell of a DefineShape tag + /*! Only the bounds rect is passed. Fills, Lines, and + Shapes must be added. + + \param _rect the bounds rectangle + */ + FDTDefineShape3(); + FDTDefineShape3(FRect *_rect); + void setBounds(FRect *_rect); + virtual ~FDTDefineShape3(); + //! Add a Shape record to the internal ShapeRec array + /*! Use FShapeRecChange, FShapeRecEdgeStraight, FShapeRecEdgeStraight + to create edges to add to this shape + \param _shapeRec + */ + void AddShapeRec(FShapeRec *_shapeRec); + //! Cleans up the internal representation of the fill and line style arrays. + void FinishStyleArrays(void); + + //! Add fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. + \param fillStyle + \return the position in the array. When creating the shape change record later. + */ + U32 AddFillStyle(FFillStyle *fillStyle); + + //! Add solid fill style to shape, + /*! Remember to store the position of the fill style. Use this when + creating the shape change record later, to indicate which style + to use for the fill. + \param fillColor + \return the position in the array. When creating the shape change record later. + */ + U32 AddSolidFillStyle(FColor *fillColor); + + //! Add line style to array and record its index. + /*! Remember to store the position of the fill style. Use this + when creating the shape change record later, to indicate which style + to use for the next edges. + \param lineWidth Line thickness in twips. + \param lineColor Line color. (should be RGBA type). + \return the position in the array. + */ + U32 AddLineStyle(U32 lineWidth, FColor *lineColor); + + //! Record objects's ID for when you do a PlaceObject + U16 ID(void); + //! Called by the FObj to write to the output stream + /*! \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + virtual void SetId(U16 id) { characterID = id; } + bool IsDefineShape(); + void changeColor(const FColor &oldColor, const FColor &newColor); + void changeColor(const std::map &table); + + FRect GetBBox() const { return *shapeBounds; } +private: + U16 characterID; + FRect *shapeBounds; + FShapeWStyle *shapeWithStyle; + FFillStyleArray *fillStyleArray; + FLineStyleArray *lineStyleArray; + U32 nFillBits; + U32 nLineBits; + U8 styleArraysFinished; +}; + +//! Abstract Base Class. All edge records fall into this category +/*! The fill classes from the solid, gradient, bitmap are children. + + \sa FFillStyleGradient, FFillStyleBitmap, FFillStyleSolid + */ +class DVAPI FAFillStyle +{ +public: + virtual bool IsSolidStyle() { return false; }; + + virtual ~FAFillStyle() {} + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; +}; + +//The fill style used by regular non-morph shapes + +//! Why does this exist? +/*! It is used as a parameter to other functions but I don't know why. + + \sa FFillStyleGradient, FFillStyleBitmap, FFillStyleSolid + */ +class DVAPI FFillStyle : public FAFillStyle +{ +public: + virtual ~FFillStyle() {} +}; + +//!An array of fill styles. +/*! + + \sa FFillStyleGradient, FFillStyleBitmap, FFillStyleSolid, FAFillStyle +*/ +class DVAPI FFillStyleArray +{ + +public: + FFillStyleArray(void) {} + virtual ~FFillStyleArray(void); + //! The given fill style is added to the end of the fill style array. + /*! The position of the added fill style is returned so that the fill style can later be referenced. + + \param fillStyle the style to add + */ + + U32 Add(FAFillStyle *fillStyle); + //! Returns the size of the fill style list. + U32 Size(); + FAFillStyle *get(unsigned int index); + + //! Writes the array to the stream + /*! Travels through all of the nodes in the array and writing + their fill styles. First has to write the count of fill style arrays. See's if count + is small enough to fit in to an 8 bit field, and either writes the count into an 8 bit + field, or writes all 1's into an 8 bit field and writes the real count into a 16 bit + field. + + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + //the list which contains all of the fill styles + std::vector fillStyleArray; +}; + +//! The Bitmap fill style used by regular non-morph shapes +/*! Can be tiled or clipped, depending on the flag. + + \sa FFillStyleArray, FFillStyleGradient, FFillStyleBitmap, FFillStyleSolid, FAFillStyle + */ +// +// + +class DVAPI FFillStyleBitmap : public FFillStyle +{ + +public: + /*! + + \param tiled indicates if the Bitmap fill style is tiled (tiledFlag==1) or + clipped (tiledFlag==0). + \param matrix translation matrix for offsetting, rotating, scaleing etc. the bitmap + */ + FFillStyleBitmap(int tiled, U16 ID, FMatrix *matrix); + virtual ~FFillStyleBitmap(void); + //! Writes the bitmap fill style to the given FSWFStream. + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + int tiledFlag; + U16 bitmapID; + FMatrix *bitmapMatrix; +}; + +//! The Gradient fill style used by regular non-morph shapes +/*! Can be linear or radial, depending on the flag. + + \sa FFillStyleArray, FGradient + */ +class DVAPI FFillStyleGradient : public FFillStyle +{ + +public: + //! Create a Gradient fill style + /*! + \param linear indicates if the gradient fill style is linear (linearFlag==1) or + radial (linearFlag==0). + \param matrix matrix translation matrix for placing the gradient in the fill area + Not sure what each of the matrix fields do??? + \param gradient a pre-build gradient object. See FGradient + */ + FFillStyleGradient(int linear, FMatrix *matrix, FGradient *gradient); + virtual ~FFillStyleGradient(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + int linearFlag; + FMatrix *gradFillMatrix; + FGradient *gradFill; +}; + +//The solid fill style used by regular non-morph shapes + +//! Creates a Solid Fill object +/*! Basically just contains a color + + \sa FFillStyleArray, FFillStyleGradient, FFillStyleBitmap + */ +class DVAPI FFillStyleSolid : public FFillStyle +{ +public: + //! Create a style object + /*! Whether or not FColor contains alpha information is not + important until the color is written out. + + \param Fcolor which can contain alpha or not + */ + FFillStyleSolid(FColor *_color); + + virtual ~FFillStyleSolid(); + + //! Write the color to the file stream + /*! When this object is serialized, it automatically handles the case of + including the alpha information if its there. + \param *_SWFStream the output stream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + bool IsSolidStyle() { return true; }; + void setColor(FColor *_color) + { + delete color; + color = _color; + } + FColor *getColor() { return color; } + +private: + FColor *color; +}; + +//! Gradient information is stored in an array of Gradient Records +/*! This is that array. This style of gradient records is used with + non-morphing objects. + + \sa FGradRecord + */ +class DVAPI FGradient +{ + +public: + FGradient(void); + ~FGradient(void); + //! Add a Gradient Record to the internal list + /*! Up to 8 gradient records may be added to this list + The ratio field within each GradientRec determines what percentage + of a gradient is a particular color. + \param FAGradRecord* pointer to a gradient record + */ + void Add(FAGradRecord *g); + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + //the list which contains all of the grad records + std::list gradRecords; +}; + +// used by non-morph shapes + +//! A non morph gradient record. +/*! Note that ostensibly the ratio parameter is the percentage complete aTLong + the gradient that this color should be but it's really not known what this + field is. + \sa FGradient, FFillStyleGradient + */ +class DVAPI FGradRecord : public FAGradRecord +{ +public: + //!Create the Gradient record with the mysterious ration parm. + /*! + + \param U_ratio know one knows for sure + \param FColor one of the colors to shift through + */ + FGradRecord(U32 _ratio, FColor *color); + + virtual ~FGradRecord(); + + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 ratio; + FColor *color; +}; + +// + +//! The line style used by regular non-morph shapes +/*! LineStyles are held in arrays of LineStyles which are held + in DefineShape Tags. + + \sa FExampleRectangle.cpp, FLineStyleArray + */ +class DVAPI FLineStyle : public FALineStyle +{ + +public: + //!Line Style is just a thickness and color + /*! + + \param width U32 + \param _color FColor + */ + FLineStyle(U32 _width, FColor *_color); + virtual ~FLineStyle(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 width; + FColor *color; +}; + +// +//! An array of line styles. +/*! LineStyles are held in arrays of LineStyles which are held + in DefineShape Tags. + + \sa FLineStyle, FExampleRectangle.cpp +*/ +class DVAPI FLineStyleArray +{ + +public: + //!Create an empty initialized array + FLineStyleArray(void); + ~FLineStyleArray(void); + //!Add the line style to the array. + /*! Automatically handles the case of packing the corresponding SWF object + properly when you go over 255 styles. Not that we can see why you'd do + this. + + \param lineStyle the style object to add to the internal array + */ + U32 Add(FALineStyle *lineStyle); + //! The number of items in the list + /*! This is needed when a containing DefineShape needs to know how how + many styles there are and therefore how many bits are needed for an index. + The given line style is added to the end of the line style array. The position of + the added line style is returned so that the line style can later be referenced. + */ + U32 Size(void); + //! Write the array to the output stream. + /*! Writes to the stream by travelling through all of the nodes in + the array and writing their line styles. First has to write the count + of line style arrays. Sees if count is small enough to fit in to an + 8 bit field, and either writes the count into an 8 bit field, or writes + all 1's into an 8 bit field and writes the real count into a 16 bit field. + + \param *_SWFStream + */ + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + //the list which contains all of the line styles + std::list lineStyleArray; +}; + +//! The Base class of fill style objects used by morph shapes +/*! + + \sa FMorphFillStyleGradient, FMorphFillStyleBitmap, FMorphFillStyleSolid + */ +class DVAPI FMorphFillStyle : public FAFillStyle +{ +public: + virtual ~FMorphFillStyle() {} + virtual void WriteToSWFStream(FSWFStream * /* _SWFStream */) {} +}; + +// +// + +//!The Bitmap fill style used by morph shapes +/*! Note that all Morph fill styles are documented in SWF as a single overloaded + data structure. It's simplier to have three separate routiens. + + This can be tiled or clipped, depending on the flag. + + \sa FMorphFillStyleGradient, FMorphFillStyleBitmap, FMorphFillStyleSolid, FMatrix + */ +class DVAPI FMorphFillStyleBitmap : public FMorphFillStyle +{ + +public: + // The tiledFlag indicates if the Bitmap fill style is tiled (tiledFlag==1) or + // clipped (tiledFlag==0). + + //! Bitmap fill style for a morph + /*! + + \param tiled The tiledFlag indicates if the Bitmap fill style is tiled (tiledFlag==1) or + clipped (tiledFlag==0) + \param ID Character ID of bitmap + \param matrix1 rotation/translation Matrix for first state + \param matrix2 for the second state + */ + FMorphFillStyleBitmap(int tiled, U16 ID, FMatrix *matrix1, FMatrix *matrix2); + virtual ~FMorphFillStyleBitmap(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + int tiledFlag; + U16 bitmapID; + FMatrix *bitmapMatrix1; + FMatrix *bitmapMatrix2; +}; + +//!The Gradient fill style used by morph shapes +/*! Note that all Morph fill styles are documented in SWF as a single overloaded + data structure. It's simplier to have three separate routines. + + Can be linear or radial, depending on the flag, depending on the flag. + + \sa FMorphFillStyleGradient, FMorphFillStyleBitmap, FMorphFillStyleSolid, FGradient, FMatrix, FFillStyleArray + */ +class DVAPI FMorphFillStyleGradient : public FMorphFillStyle +{ + +public: + //!Construct a Gradient Fill Style + /*! Ultimatly to go in a MorphFillStyles Array and then get + added to a DefineMorphShape + + \param linear The linearFlag indicates if the gradient fill + style is linear (linearFlag==1) or radial (linearFlag==0) + \param matrix1 + \param matrix2 translation matrix for the gradient before and after. + \param gradient an FGradient fill + */ + FMorphFillStyleGradient(int linear, FMatrix *matrix1, FMatrix *matrix2, FGradient *gradient); + virtual ~FMorphFillStyleGradient(void); + //!Writes the Gradient fill style to the given FSWFStream. + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + int linearFlag; + FMatrix *gradFillMatrix1; + FMatrix *gradFillMatrix2; + FGradient *gradFill; +}; + +//!The solid fill style used by morph shapes +/*! Each Fill style has two colors for the two objects being morphed. The FillStyles are +in a FFillStyle array. In this case the fill styles object 1 and 2 are interleaved. The +FillStyleArray is then stored in a DefineMorphShape. + + \sa FMorphFillStyleGradient, FMorphFillStyleBitmap, FMorphFillStyleSolid, FGradient, FMatrix,FFillStyleArray + */ +class DVAPI FMorphFillStyleSolid : public FMorphFillStyle +{ + +public: + /*! + \param _color1 Solid fill color with transparency information for first shape + \param _color2 Same for second + */ + FMorphFillStyleSolid(FColor *_color1, FColor *_color2); + virtual ~FMorphFillStyleSolid(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FColor *color1; + FColor *color2; +}; + +//!The grad record used by morph shapes +/*! The Morph Gradient store an array of these records. Each is like a double +of the regular Gradient fill record. Each Morph Gradient Record has two colors +and two ratio numbers for the two objects being morphed. Theses Gradient Records +possibly 8 of them, are stored in an FAGradient array, the base of all gradient records. +Normally the Colors in a gradient array store the colors aTLong the gradient. These +store pairs of colors, one for each of the two objects aTLong with the mysterious ratio number. + The FMorphFillStyleGradient is then stored in a DefineMorphShape. + + \sa FMorphFillStyleGradient, FMorphFillStyleBitmap, FMorphFillStyleSolid, FAGradient, FMatrix,FFillStyleArray + */ +class DVAPI FMorphGradRecord : public FAGradRecord +{ + +public: + //!Create a Morph Gradient record + /*! + \param _ratio1 for the first object + \param _color1 + \param _ratio2 for the second object + \param _color2 + */ + FMorphGradRecord(U32 _ratio1, FColor *_color1, U32 _ratio2, FColor *_color2); + virtual ~FMorphGradRecord(); + + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 ratio1; + U32 ratio2; + + FColor *color1; + FColor *color2; +}; + +//! Morph line styles to go in the DefineMorph Object +/*! MorphLineStyle is an FALineStyle. These get put into + FLineStyleArrays which in turn are put in the Define Morph Object. + These are just line regular line styles, except that they + carry a pair of line styles, one for the morph from object + and the corresponding one in the Morphe to object. + + \sa FLineStyleArray + */ +class FMorphLineStyle : public FALineStyle +{ + +public: + //!create the pair of styles, width/color + /*! + + \param width1 + \param width2 + \param _color1 + \param _color2 + + */ + FMorphLineStyle(U32 _width1, U32 _width2, FColor *_color1, FColor *_color2); + virtual ~FMorphLineStyle(); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FColor *color1; + FColor *color2; + U32 width1; + U32 width2; +}; + +//! Virtual Base class for the change, straight, and curved Shape records +/*! + + \sa FShapeRecChange, FShapeRecEdgeStraight, FShapeRecEdgeCurved + */ +class DVAPI FShapeRec +{ + +public: + virtual ~FShapeRec() {} + virtual bool isFShapeRecChange() { return false; } + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; + virtual void IncludeNFillBitInfo(U32 _nFillBits) = 0; + virtual void IncludeNLineBitInfo(U32 _nLineBits) = 0; + virtual bool IsRecChange() { return false; } +}; + +// shape record that defines changes in line style, fill style, position, or a new set of styles + +class DVAPI FShapeRecChange : public FShapeRec +{ + +public: + FShapeRecChange(U32 _stateNewStyles, + U32 _stateLineStyle, + U32 _stateFillStyle1, + U32 _stateFillStyle0, + U32 _stateMoveTo, + S32 _moveDeltaX, + S32 _moveDeltaY, + U32 _fill0Style, + U32 _fill1Style, + U32 _lineStyle, + FFillStyleArray *_fillStyles, + FLineStyleArray *_lineStyles); + + virtual ~FShapeRecChange(void); + + virtual bool isFShapeRecChange() { return true; } + void getFillLineBits(U32 &_nFillBits, U32 &_nLineBits) + { + _nFillBits = nFillBits; + _nLineBits = nLineBits; + } + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + virtual void IncludeNFillBitInfo(U32 _nFillBits); + virtual void IncludeNLineBitInfo(U32 _nLineBits); + bool IsRecChange() { return true; } + FFillStyleArray *GetFillStyles(); + +private: + U32 stateNewStyles; + U32 stateLineStyle; + U32 stateFillStyle0; + U32 stateFillStyle1; + U32 stateMoveTo; + U32 nMoveBits; + U32 nFillBits; // these two values are stored in the Shape field + U32 nLineBits; // they are passed to each ShapeRec + S32 moveDeltaX; + S32 moveDeltaY; + U32 fill0Style; + U32 fill1Style; + U32 lineStyle; + FFillStyleArray *fillStyles; + FLineStyleArray *lineStyles; + U32 MinBits(void); +}; + +//! Create a straight edge +/*! The XY values are passed as delta values from the previous XY points. + Creating and adding several straight edges in a row will create a set + of connected edges. + + This may change to absolution positions in the future. ??? + + See also FExampleRectangle.cpp + + */ +class DVAPI FShapeRecEdgeStraight : public FShapeRec +{ +public: + //! Create a straight edge from the previous point + /*! This is the equivalent of a "LineTo" command + + \param generalDX + \param generalDY + */ + FShapeRecEdgeStraight(S32 dx, S32 dy); + virtual ~FShapeRecEdgeStraight(){}; + + //!Write out as Vertical, Horizontal or general line SWF Record automatically + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNFillBitInfo(U32 _nFillBits); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNLineBitInfo(U32 _nLineBits); + +private: + U32 edgeFlag; + U32 nBits; + U32 generalLineFlag; + S32 generalDeltaX; + S32 generalDeltaY; + U32 verticalLineFlag; + S32 horizontalDeltaX; + S32 verticalDeltaY; + U32 MinBits(void); +}; + +//! Create a bezier edge +/*! The control points are passed as delta values from the previous control points. + This may change to absolution positions in the future. ??? + + See also [example file], [bezier info file] + + */ +class DVAPI FShapeRecEdgeCurved : public FShapeRec +{ +public: + //! Create a bezier + /*! All values expressed in twips ??? + \param controlDX delta from the last X control value + \param controlDY delta from the last Y control value + \param anchorDX delta from the last X anchor value + \param anchorDY delta from the last Y anchor value + */ + FShapeRecEdgeCurved(S32 controlDX, S32 controlDY, S32 anchorDX, S32 anchorDY); + virtual ~FShapeRecEdgeCurved(void) {} + //! Called by containing Shape when file is serialized + /*! Writes the anchor, control, pts, as well as the minBits and edgeFlag + \param _SWFStream + */ + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNFillBitInfo(U32 _nFillBits); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNLineBitInfo(U32 _nLineBits); + +private: + U32 edgeFlag; + U32 nBits; + S32 controlDeltaX; + S32 controlDeltaY; + S32 anchorDeltaX; + S32 anchorDeltaY; + U32 MinBits(void); +}; + +//The shape record which signifies the end of a shape record array. + +class DVAPI FShapeRecEnd : public FShapeRec +{ + +public: + FShapeRecEnd(void); + //!Write an EndRecord. Signifies the last ShapeRecord in the ShapeRecArray + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNFillBitInfo(U32 _nFillBits); + //!dummy in this object. This call only makes sense for a Change Rec, here for historical + virtual void IncludeNLineBitInfo(U32 _nLineBits); +}; + +class DVAPI FShapeWStyle +{ + +public: + FShapeWStyle(FFillStyleArray *_fillStyles, FLineStyleArray *_lineStyles); + ~FShapeWStyle(void); + void AddShapeRec(FShapeRec *shapeRec); + void WriteToSWFStream(FSWFStream *_SWFStream); + U32 NumFillBits(); + U32 NumLineBits(); + FFillStyleArray *GetFillStyles() { return fillStyles; } + void changeColor(const FColor &oldColor, const FColor &newColor); + void changeColor(const std::map &table); + +private: + FFillStyleArray *fillStyles; + FLineStyleArray *lineStyles; + U32 nFillBits; + U32 nLineBits; + std::vector shapeRecs; //see comments, will need to be changed +}; + +#ifdef WIN32 // added from DV +#pragma warning(pop) +#endif +#endif diff --git a/toonz/sources/common/flash/FDTSounds.cpp b/toonz/sources/common/flash/FDTSounds.cpp new file mode 100644 index 0000000..67617ab --- /dev/null +++ b/toonz/sources/common/flash/FDTSounds.cpp @@ -0,0 +1,483 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTSounds.cpp + + This source file contains the definition for all low-level sound-related functions, + grouped by classes: + + Class Member Function + + FDTDefineSound FDTDefineSound() + void WriteToSWFStream(FSWFStream*); + + FDTDefineSoundADPCM FDTDefineSoundADPCM(U8, U8, U8, U32, U8*, U32, int); + + FDTDefineSoundWAV FDTDefineSoundWAV(FILE*, int); + bool loadWavFile(FILE*, SSound*, U8**, int*); + FDTSoundStreamBlock FDTSoundStreamBlock(U32, U8*); + void WriteToSWFStream(FSWFStream*); + + FDTSoundStreamHead FDTSoundStreamHead(U8, U8, U16); + void WriteToSWFStream(FSWFStream*); + + FDTSoundStreamHead2 FDTSoundStreamHead2(U8, U8, U8, U8, U16); + void WriteToSWFStream(FSWFStream*); + + FSndEnv FSndEnv(U32, U16, U16); + void WriteToSWFStream(FSWFStream*); + + FSoundInfo FSoundInfo(U8, U8, U8, U8, U8, U32, U32, U16, U8, FSndEnv*); + ~FSoundInfo(); + void WriteToSWFStream(FSWFStream*); + +****************************************************************************************/ + +#include "FDTSounds.h" +#include "FSound.h" +#include "FSWFStream.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineSound -------------------------------------------------------- + +FDTDefineSound::FDTDefineSound() +{ + soundID = FObjCollection::Increment(); + memset(&soundData, 0, sizeof(soundData)); +} + +void FDTDefineSound::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream tempBuffer; + + tempBuffer.WriteWord((U32)soundID); + tempBuffer.WriteBits((U32)soundData.format, 4); + tempBuffer.WriteBits((U32)soundData.rate, 2); + tempBuffer.WriteBits((U32)soundData.size, 1); + tempBuffer.WriteBits((U32)soundData.type, 1); + tempBuffer.WriteDWord(soundData.sampleCount); + tempBuffer.WriteLargeData(soundData.sound, soundData.soundSize); + + _SWFStream->AppendTag(stagDefineSound, tempBuffer.Size(), &tempBuffer); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineSoundADPCM --------------------------------------------------- + +FDTDefineSoundADPCM::FDTDefineSoundADPCM(U8 rate, + U8 size, + U8 type, + U32 sampleCount, + const U8 *wavData, + U32 wavDataSize, + int compression) +{ + // clear the vector to write to + pcmData.clear(); + + soundData.format = 1; //a 1 indicates ADPCM + soundData.rate = rate; + soundData.size = size; + soundData.type = type; + soundData.sampleCount = sampleCount; + + // Construct a FSound object + FSound sound; + sound.format = Format(); + + sound.nSamples = (S16)soundData.sampleCount; + sound.samples = const_cast(wavData); // it won't change it - so we cast (lee) + sound.dataLen = (S16)wavDataSize; + sound.delay = 0; + + FSoundComp compress(&sound, compression); + + compress.Compress(const_cast(wavData), sound.nSamples, &pcmData); // it won't change it - so we cast (lee) + compress.Flush(&pcmData); + + // store the compressed data to the sound structure + soundData.sound = &pcmData[0]; + soundData.soundSize = pcmData.size(); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineSoundWAV ----------------------------------------------------- + +FDTDefineSoundWAV::FDTDefineSoundWAV(FILE *fp, int comp) +{ + U8 *wavData; // memory where the WAV file is stored + int wavDataSize; // the number of bytes in the WAV file + + // clear the vector to write to + pcmData.clear(); + + // fp = fopen( waveFile, "rb" ); + if (!fp) { + return; + } + + if (loadWavFile(fp, &soundData, &wavData, &wavDataSize) == false) { + // there was an error + return; + } + + // Construct a FSound object + FSound sound; + sound.format = Format(); + + sound.nSamples = (S16)soundData.sampleCount; + sound.samples = wavData; + sound.dataLen = wavDataSize; + sound.delay = 0; + + FSoundComp compress(&sound, comp); + + compress.Compress(wavData, sound.nSamples, &pcmData); + compress.Flush(&pcmData); + + // fclose( fp ); + + // store the compressed data to the sound structure + soundData.sound = &pcmData[0]; + soundData.soundSize = pcmData.size(); + + // delete the wav data + delete[] wavData; +} + +//------------------------------------------------------------------------------ +bool FDTDefineSoundWAV::loadWavFile(FILE *fp, SSound *soundData, U8 **wavData, int *wavDataSize) +{ + TUINT32 nextseek; + TUINT32 filesize; + TUINT32 fileTag; + TUINT32 typeTag; + TUINT32 chunkSize; + TUINT32 chunkId; + TUINT32 formatTag = 0; + + assert(fp); + memset(soundData, 0, sizeof(SSound)); + + soundData->format = 1; // ADPCM compressed + + fileTag = read32(fp); + if (fileTag != 0x46464952) { + FLASHOUTPUT("Not a RIFF format file\n"); + return false; + } + + filesize = read32(fp); + filesize += ftell(fp); + + typeTag = read32(fp); + if (typeTag != 0x45564157) { + FLASHOUTPUT("Not a WAVE file\n"); + return false; + } + + nextseek = ftell(fp); + while (nextseek < filesize) { + fseek(fp, (TINT32)nextseek, 0); + chunkId = read32(fp); + chunkSize = read32(fp); + nextseek = chunkSize + ftell(fp); + +#ifndef __LP64__ + FLASHOUTPUT("\n----- Chunk ID %d, Size %d -----\n", chunkId, chunkSize); +#else + FLASHOUTPUT("\n----- Chunk ID %d, Size %d -----\n", chunkId, chunkSize); +#endif + + switch (chunkId) { + case 0x20746D66: //Format Chunk + { + FLASHOUTPUT("Format Chunk\n"); + FLASHOUTPUT("Common Fields:\n"); + + formatTag = read16(fp); +#ifndef __LP64__ + FLASHOUTPUT(" Format Tag %04lXh: ", (long unsigned int)formatTag); +#else + FLASHOUTPUT(" Format Tag %04Xh: ", formatTag); +#endif + switch (formatTag) { + default: + case 0x0000: + FLASHOUTPUT("WAVE_FORMAT_UNKNOWN"); + break; + case 0x0001: + FLASHOUTPUT("WAVE_FORMAT_PCM\n"); + break; + case 0x0002: + FLASHOUTPUT("WAVE_FORMAT_ADPCM\n"); + break; + case 0x0005: + FLASHOUTPUT("WAVE_FORMAT_IBM_CVSD\n"); + break; + case 0x0006: + FLASHOUTPUT("WAVE_FORMAT_ALAW\n"); + break; + case 0x0007: + FLASHOUTPUT("WAVE_FORMAT_MULAW\n"); + break; + case 0x0010: + FLASHOUTPUT("WAVE_FORMAT_OKI_ADPCM"); + break; + case 0x0011: + FLASHOUTPUT("WAVE_FORMAT_DVI_ADPCM or WAVE_FORMAT_IMA_ADPCM\n"); + break; + case 0x0015: + FLASHOUTPUT("WAVE_FORMAT_DIGISTD\n"); + break; + case 0x0016: + FLASHOUTPUT("WAVE_FORMAT_DIGIFIX\n"); + break; + case 0x0020: + FLASHOUTPUT("WAVE_FORMAT_YAMAHA_ADPCM\n"); + break; + case 0x0021: + FLASHOUTPUT("WAVE_FORMAT_SONARC\n"); + break; + case 0x0022: + FLASHOUTPUT("WAVE_FORMAT_DSPGROUP_TRUESPEECH\n"); + break; + case 0x0023: + FLASHOUTPUT("WAVE_FORMAT_ECHOSC1\n"); + break; + case 0x0024: + FLASHOUTPUT("WAVE_FORMAT_AUDIOFILE_AF18\n"); + break; + case 0x0101: + FLASHOUTPUT("IBM_FORMAT_MULAW\n"); + break; + case 0x0102: + FLASHOUTPUT("IBM_FORMAT_ALAW\n"); + break; + case 0x0103: + FLASHOUTPUT("IBM_FORMAT_ADPCM\n"); + break; + case 0x0200: + FLASHOUTPUT("WAVE_FORMAT_CREATIVE_ADPCM\n"); + break; + } + int channels = (int)read16(fp); + soundData->type = channels - 1; + FLASHOUTPUT(" %u Channels\n", channels); + + int samplesPerSec = (int)read32(fp); + FLASHOUTPUT(" %u Samples Per Second\n", samplesPerSec); + switch (samplesPerSec / 5000) { + case 1: // 5k + soundData->rate = Snd5k; + break; + case 2: // 11k + soundData->rate = Snd11k; + break; + case 4: // 22k + soundData->rate = Snd22k; + break; + case 8: // 44k + soundData->rate = Snd44k; + break; + default: + FLASHOUTPUT("Sample rate %d unsupported\n", samplesPerSec); + return false; + } + + int bytesPerSec = (int)read32(fp); + FLASHOUTPUT(" %u Average Bytes Per Second\n", bytesPerSec); + + int blockAlign = read16(fp); + FLASHOUTPUT(" Block Align %u\n", blockAlign); + + FLASHOUTPUT("Format Specific Fields:\n"); + int bits = read16(fp); + FLASHOUTPUT(" %u Bits Per Sample\n", bits); + + //store the size of the chunks of data (8v16 bit) + if (bits == 8) + soundData->size = 0; + else if (bits == 16) + soundData->size = 1; + else + FLASHASSERT(0); + + if (formatTag != 0x0001) { + int extra = read16(fp); + FLASHOUTPUT(" %d Bytes of extra information\n", extra); + FLASHOUTPUT(" NOT YET SUPPORTED\n"); + return false; + } + } break; + + case 0x61746164: //Data Chunk + { + FLASHOUTPUT("Data Chunk\n"); + if (!formatTag) { + FLASHOUTPUT("Warning Format Chunk not defined before Data Chunk\n"); + return false; + } + *wavDataSize = (int)chunkSize; + soundData->sampleCount = chunkSize / (soundData->size + 1); + *wavData = new U8[chunkSize]; + int read = fread(*wavData, 1, *wavDataSize, fp); + + if (read != (*wavDataSize)) { + FLASHOUTPUT("Warning Read %d of %d bytes\n", read, (*wavDataSize)); + return false; + } + return true; + } + + default: + FLASHOUTPUT("Unknown Chunk\n"); + return false; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTSoundStreamBlock --------------------------------------------------- + +FDTSoundStreamBlock::FDTSoundStreamBlock(U32 _size, U8 *_data) +{ + size = _size; + data = _data; +} + +void FDTSoundStreamBlock::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + body.WriteLargeData(data, size); + + _SWFStream->AppendTag(stagSoundStreamBlock, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTSoundStreamHead ---------------------------------------------------- + +FDTSoundStreamHead::FDTSoundStreamHead(U8 _mixFormat, U8 _soundType, U16 _count) +{ + mixFormat = _mixFormat; + soundType = _soundType; + count = _count; +} + +void FDTSoundStreamHead::WriteToSWFStream(FSWFStream *_SWFStream) +{ + FSWFStream body; + + body.WriteByte((U32)mixFormat); + body.WriteBits(1, 4); + body.WriteBits(0, 2); + body.WriteBits(1, 1); + body.WriteBits((U32)soundType, 1); + body.WriteWord((U32)count); + _SWFStream->AppendTag(stagSoundStreamHead, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTSoundStreamHead2 --------------------------------------------------- + +FDTSoundStreamHead2::FDTSoundStreamHead2(U8 _mixFormat, U8 _compression, U8 _size, U8 _soundType, U16 _count) +{ + mixFormat = _mixFormat; + compression = _compression; + rate = 0; + size = _size; + soundType = _soundType; + count = _count; +} + +FDTSoundStreamHead2::FDTSoundStreamHead2(U8 _mixFormat, U8 _compression, U8 _rate, U8 _size, U8 _soundType, U16 _count) +{ + mixFormat = _mixFormat; + compression = _compression; + rate = _rate; + size = _size; + soundType = _soundType; + count = _count; +} + +void FDTSoundStreamHead2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteByte((U32)mixFormat); + body.WriteBits((U32)compression, 4); + body.WriteBits((U32)rate, 2); + body.WriteBits((U32)size, 1); + body.WriteBits((U32)soundType, 1); + body.WriteWord((U32)count); + + _SWFStream->AppendTag(stagSoundStreamHead2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FSndEnv --------------------------------------------------------------- +FSndEnv::FSndEnv(U32 mark44, U16 level0, U16 level1) +{ + mark44 = mark44; + level0 = level0; + level1 = level1; +} + +void FSndEnv::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteDWord(mark44); + _SWFStream->WriteWord((U32)level0); + _SWFStream->WriteWord((U32)level1); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FSoundInfo ------------------------------------------------------------ +FSoundInfo::FSoundInfo(U8 _syncFlags, U8 _hasEnvelope, U8 _hasLoops, U8 _hasOutPoint, + U8 _hasInPoint, U32 _inPoint, U32 _outPoint, U16 _loopCount, + U8 _nPoints, FSndEnv *_soundEnvelope) +{ + + syncFlags = _syncFlags; + hasEnvelope = _hasEnvelope; + hasLoops = _hasLoops; + hasOutPoint = _hasOutPoint; + hasInPoint = _hasInPoint; + inPoint = _inPoint; + outPoint = _outPoint; + loopCount = _loopCount; + nPoints = _nPoints; + soundEnvelope = _soundEnvelope; +} + +FSoundInfo::~FSoundInfo() +{ + + delete soundEnvelope; +} + +void FSoundInfo::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteBits((U32)syncFlags, 4); + _SWFStream->WriteBits((U32)hasEnvelope, 1); + _SWFStream->WriteBits((U32)hasLoops, 1); + _SWFStream->WriteBits((U32)hasOutPoint, 1); + _SWFStream->WriteBits((U32)hasInPoint, 1); + if (hasInPoint) + _SWFStream->WriteDWord(inPoint); + if (hasOutPoint) + _SWFStream->WriteDWord(outPoint); + if (hasLoops) + _SWFStream->WriteWord(loopCount); + if (hasEnvelope) { + _SWFStream->WriteByte(nPoints); + soundEnvelope->WriteToSWFStream(_SWFStream); + } +} diff --git a/toonz/sources/common/flash/FDTSounds.h b/toonz/sources/common/flash/FDTSounds.h new file mode 100644 index 0000000..655f334 --- /dev/null +++ b/toonz/sources/common/flash/FDTSounds.h @@ -0,0 +1,216 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTSounds.h + + This header-file contains the declarations of all low-level sound-related classes. + Their parent classes are in the parentheses: + + class FDTDefineSound; (public FDT) + class FDTDefineSoundADPCM; (public FDTDefineSound) + class FDTDefineSoundWAV; (public FDTDefineSound) + class FDTSoundStreamBlock; (public FDT) + class FDTSoundStreamHead; (public FDT) + class FDTSoundStreamHead2; (public FDT) + class FSndEnv; + class FSoundInfo; + +****************************************************************************************/ + +#ifndef _F_DEFINE_SOUNDS_H_ +#define _F_DEFINE_SOUNDS_H_ + +#include "Macromedia.h" +#include "FDT.h" + +#include + +// A flash object that defines a sound + +class FDTDefineSound : public FDT +{ +public: + // Compression Type + enum { + NO_COMPRESSION, + PCM + }; + + // Compression Level + enum { + Comp2 = 2, + Comp3, + Comp4, + Comp5 + }; + + // Sound Rate + enum { + Snd5k, + Snd11k, + Snd22k, + Snd44k + }; + + // Sound Size + enum { + Snd8Bit, + Snd16Bit + }; + + // Sound Type + enum { + Snd16Mono, + Snd16Stereo + }; + + FDTDefineSound(); + virtual ~FDTDefineSound() {} + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + + U16 ID() { return soundID; } + int NumSamples() { return (U8)soundData.sampleCount; } + int Format() { return 4 * (soundData.rate) + (soundData.size) * 2 + (soundData.type); } + int SoundType() { return soundData.type; } + +protected: + U16 soundID; + SSound soundData; + std::vector pcmData; +}; + +// A flash object that defines a sound + +class FDTDefineSoundADPCM : public FDTDefineSound +{ +public: + FDTDefineSoundADPCM(U8 rate, U8 size, U8 type, U32 sampleCount, + const U8 *data, U32 dataSize, int compression); +}; + +// A flash object that defines a sound +class FDTDefineSoundWAV : public FDTDefineSound +{ + +public: + FDTDefineSoundWAV(FILE *wavFile, int compression); + +private: + bool loadWavFile(FILE *fp, + SSound *soundData, + U8 **wavData, + int *wavDataSize); + U32 read32(FILE *fp) + { + U32 val; + fread(&val, 1, 4, fp); + return val; + } + U16 read16(FILE *fp) + { + U16 val; + fread(&val, 1, 2, fp); + return val; + } +}; + +// A Flash object that defines sound data that is interleaved with frame data +//enables sound streaming + +class FDTSoundStreamBlock : public FDT +{ + +public: + FDTSoundStreamBlock(U32 _size, U8 *_data); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 size; + U8 *data; +}; + +// A flash object that defines the start of sound data that will be interleaved within flash frames +// This is how sound streaming of networks is possible. +// Doesn't support compressed sound (flash 2.0) + +class FDTSoundStreamHead : public FDT +{ + +public: + FDTSoundStreamHead(U8 _mixFormat, // mixer format (?) set to 6 + U8 _soundType, // 0 mono, 1 stereo + U16 _count // number of sound samples + ); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 mixFormat; + U8 soundType; + U16 count; +}; + +class FDTSoundStreamHead2 : public FDT +{ + +public: + FDTSoundStreamHead2(U8 _mixFormat, U8 _compression, U8 _size, U8 _soundType, U16 _count); + FDTSoundStreamHead2(U8 _mixFormat, U8 _compression, U8 _rate, U8 _size, U8 _soundType, U16 _count); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U8 mixFormat; + U8 compression; + U8 rate; + U8 size; + U8 soundType; + U16 count; +}; + +class FSndEnv +{ + +public: + FSndEnv(U32 mark44, U16 level0, U16 level1); + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 mark44; + U16 level0; + U16 level1; +}; + +// specifies how a sound character is player + +class FSoundInfo +{ + +public: + FSoundInfo(U8 _syncFlags, U8 _hasEnvelope, U8 _hasLoops, U8 _hasOutPoint, + U8 _hasInPoint, U32 _inPoint, U32 _outPoint, U16 _loopCount, + U8 _nPoints, FSndEnv *_soundEnvelope); + ~FSoundInfo(); + void WriteToSWFStream(FSWFStream *_SWFStream); + + enum { + SyncNoMultiple = 0x01, + SyncStor = 0x02 + }; + +private: + U8 syncFlags; + U8 hasEnvelope; + U8 hasLoops; + U8 hasOutPoint; + U8 hasInPoint; + U32 inPoint; + U32 outPoint; + U16 loopCount; + U8 nPoints; + FSndEnv *soundEnvelope; +}; + +#endif diff --git a/toonz/sources/common/flash/FDTSprite.cpp b/toonz/sources/common/flash/FDTSprite.cpp new file mode 100644 index 0000000..88db2b8 --- /dev/null +++ b/toonz/sources/common/flash/FDTSprite.cpp @@ -0,0 +1,74 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTSprite.cpp + + This source file contains the definition for all low-level sprite-related functions, + grouped by classes: + + Class Member Function + + FDTSprite FDTSprite(); + ~FDTSprite(); + void AddFObj(FObj* ); + void WriteToSWFStream(FSWFStream* ); + + +****************************************************************************************/ +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif +#include "FSWFStream.h" +#include "FDTSprite.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTSprite ------------------------------------------------------------- + +FDTSprite::FDTSprite() +{ + characterID = FObjCollection::Increment(); + numOfFrames = 0; +} + +FDTSprite::~FDTSprite() +{ + while (!objectList.empty()) { + + delete objectList.front(); + objectList.pop_front(); + } +} + +void FDTSprite::AddFObj(FObj *_object) +{ + if (_object->IsShowFrame()) + numOfFrames++; + + objectList.push_back(_object); +} + +void FDTSprite::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream body; + + body.WriteWord((U32)characterID); + + body.WriteWord(numOfFrames); + + std::list::iterator cursor; + for (cursor = objectList.begin(); cursor != objectList.end(); cursor++) { + + (*cursor)->WriteToSWFStream(&body); + } + + // Put an end tag on the end of the temporary buffer: + body.AppendTag(stagEnd, 0, 0); + + // put the buffer into the main stream + _SWFStream->AppendTag(stagDefineSprite, body.Size(), &body); +} diff --git a/toonz/sources/common/flash/FDTSprite.h b/toonz/sources/common/flash/FDTSprite.h new file mode 100644 index 0000000..6b7b5b4 --- /dev/null +++ b/toonz/sources/common/flash/FDTSprite.h @@ -0,0 +1,54 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTSprite.h + + This header-file contains the declarations of low-level sprite-related class. + Its parent class is in the parentheses: + + class FDTSprite; (public FDT) + +****************************************************************************************/ + +#ifndef _SPRITE_H_ +#define _SPRITE_H_ + +#include +#include "Macromedia.h" +#include "FDT.h" + +//! Defines a low-level sprite object. +/*! A sprite is a flash object that acts as a "movie within a movie". + \sa FDT +*/ +class FDTSprite : public FDT +{ +public: + //! Construct a low-level sprite object. + /*! */ + FDTSprite(); + + //! Destruct a low-level sprite object. + /*! */ + virtual ~FDTSprite(); + + // Method for internal use. + void AddFObj(FObj *_object); + + // Method for internal use. + U16 ID() { return characterID; } + + // Method for internal use. + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U16 characterID; + std::list objectList; + U32 numOfFrames; +}; + +#endif diff --git a/toonz/sources/common/flash/FDTText.cpp b/toonz/sources/common/flash/FDTText.cpp new file mode 100644 index 0000000..c404b46 --- /dev/null +++ b/toonz/sources/common/flash/FDTText.cpp @@ -0,0 +1,448 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTText.cpp + + This source file contains the definition for all low-level text-related functions, + grouped by classes: + + Class Member Function + + FDTDefineEditText FDTDefineEditText(FRect*, U8, U8, U8, U8, U8, U8, U8, U8, + U8, U8, U8, U8, U16, U16, FColor*, U16, U8, U16, U16, + U16, U16, FString*, FString*); + ~FDTDefineEditText(); + void WriteToSWFStream(FSWFStream*); + + FDTDefineText FDTDefineText(FRect*, FMatrix*, int); + ~FDTDefineText(); + void AddTextRecord(FTextRecord*); + U16 ID(); + void WriteToSWFStream( FSWFStream*); + + FDTDefineText2 FDTDefineText2(FRect*, FMatrix*, int); + ~FDTDefineText2(); + void AddTextRecord(FTextRecord*); + U16 ID(); + void WriteToSWFStream(FSWFStream*); + + FTextChangeRecord FTextChangeRecord(U16, U16, U16, U16, U16, U16, FColor*, + S16, S16); + ~FTextChangeRecord() + +// FTextChangeRecord void IncludeNBitInfo(U16, U16); + void WriteToSWFStream(FSWFStream* , int, int); + + FTextGlyphRecord FTextGlyphRecord(); + ~FTextGlyphRecord(); + void AddGlyphEntry(U16, U16); + U32 MinAdvanceBits(); + U32 MinCodeBits(); + void WriteToSWFStream(FSWFStream*, int, int); + + +****************************************************************************************/ + +#include "FDTText.h" +#include "FDTFonts.h" + +// Constructor. + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineEditText ----------------------------------------------------- + +FDTDefineEditText::FDTDefineEditText(FRect *_bounds, U8 _hasFont, U8 _hasMaxLength, U8 _hasTextColor, + U8 _readOnly, U8 _password, U8 _multiline, U8 _wordWrap, U8 _hasText, + U8 _useOutlines, U8 _border, U8 _noSelect, U8 _hasLayout, U16 _fontID, + U16 _fontHeight, FColor *_textColor, U16 _maxLength, U8 _alignment, + U16 _leftMargin, U16 _rightMargin, U16 _indent, U16 _leading, + FString *_variableName, FString *_initialText) : textColor(_textColor) +{ + + bounds = _bounds; + hasFont = _hasFont; + hasMaxLength = _hasMaxLength; + hasTextColor = _hasTextColor; + readOnly = _readOnly; + password = _password; + multiline = _multiline; + wordWrap = _wordWrap; + hasText = _hasText; + useOutlines = _useOutlines; + border = _border; + noSelect = _noSelect; + hasLayout = _hasLayout; + fontID = _fontID; + fontHeight = _fontHeight; + textColor = _textColor; + maxLength = _maxLength; + alignment = _alignment; + leftMargin = _leftMargin; + rightMargin = _rightMargin; + indent = _indent; + leading = _leading; + variableName = _variableName; + initialText = _initialText; + + characterID = FObjCollection::Increment(); +} + +// deletes the FRECT and 2 FString's if they exist. + +FDTDefineEditText::~FDTDefineEditText(void) +{ + + delete bounds; + delete variableName; + delete initialText; + delete textColor; +} + +// Writes to the given FSWFStream. + +void FDTDefineEditText::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + FSWFStream tempBuffer; + + tempBuffer.WriteWord((U32)characterID); + //character ID + + //write the bounds, it's an object that knows how to write itself + bounds->WriteToSWFStream(&tempBuffer); + + //write all flags + + tempBuffer.WriteBits(hasText, 1); + tempBuffer.WriteBits(wordWrap, 1); + tempBuffer.WriteBits(multiline, 1); + tempBuffer.WriteBits(password, 1); + tempBuffer.WriteBits(readOnly, 1); + tempBuffer.WriteBits(hasTextColor, 1); + tempBuffer.WriteBits(hasMaxLength, 1); + tempBuffer.WriteBits(hasFont, 1); + + tempBuffer.WriteBits(0, 2); //unknown flags + tempBuffer.WriteBits(hasLayout, 1); + tempBuffer.WriteBits(noSelect, 1); + tempBuffer.WriteBits(border, 1); + tempBuffer.WriteBits(0, 2); //unknown flags + tempBuffer.WriteBits(useOutlines, 1); + + //tempBuffer.WriteBits(hasFont, 1); + //tempBuffer.WriteBits(hasMaxLength, 1); + //tempBuffer.WriteBits(hasTextColor, 1); + //tempBuffer.WriteBits(readOnly, 1); + //tempBuffer.WriteBits(password, 1); + //tempBuffer.WriteBits(multiline, 1); + //tempBuffer.WriteBits(wordWrap, 1); + //tempBuffer.WriteBits(hasText, 1); + //tempBuffer.WriteBits(useOutlines, 1); + //tempBuffer.WriteBits(border, 1); + //tempBuffer.WriteBits(noSelect, 1); + //tempBuffer.WriteBits(hasLayout, 1); + //tempBuffer.WriteBits(0, 4); //check on this, 3 in docs but 4 gives 16 bits + + if (hasFont) { + tempBuffer.WriteWord((U32)fontID); + tempBuffer.WriteWord((U32)fontHeight); + } + + if (hasTextColor) { // here changed. + textColor->AlphaChannel(true); + textColor->WriteToSWFStream(&tempBuffer); + } + + if (hasMaxLength) { + tempBuffer.WriteWord((U32)maxLength); + } + + if (hasLayout) { + tempBuffer.WriteByte(alignment); + tempBuffer.WriteWord(leftMargin); + tempBuffer.WriteWord(rightMargin); + tempBuffer.WriteWord(indent); + tempBuffer.WriteWord(leading); + } + + variableName->WriteToSWFStream(&tempBuffer, true); + + if (hasText) { + initialText->WriteToSWFStream(&tempBuffer, true); + } + + _SWFStream->AppendTag(stagDefineEditText, tempBuffer.Size(), &tempBuffer); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineText --------------------------------------------------------- + +FDTDefineText::FDTDefineText(FRect *_textBounds, FMatrix *_textMatrix, int glyhpsInFont) +{ + + characterID = FObjCollection::Increment(); + textBounds = _textBounds; + textMatrix = _textMatrix; + nIndexBits = (U8)FSWFStream::MinBits(glyhpsInFont, 0); +} + +FDTDefineText::~FDTDefineText() +{ + + delete textBounds; + + delete textMatrix; + + while (!textRecords.empty()) { + + delete textRecords.front(); + + textRecords.pop_front(); + } +} + +void FDTDefineText::AddTextRecord(FTextRecord *_textRec) +{ + + textRecords.push_back(_textRec); +} + +U16 FDTDefineText::ID(void) +{ + + return (U16)characterID; +} + +void FDTDefineText::WriteToSWFStream(FSWFStream *_SWFStream) +{ + U16 nAdvanceBits = 0; + U16 nCodeBits = 0; + FSWFStream body; + + body.WriteWord((U32)characterID); + + textBounds->WriteToSWFStream(&body); + textMatrix->WriteToSWFStream(&body); + + // Get the bits needed to write the advance and character codes + std::list::iterator cursor1; + for (cursor1 = textRecords.begin(); cursor1 != textRecords.end(); cursor1++) { + nAdvanceBits = (U16)std::max((*cursor1)->MinAdvanceBits(), (U32)nAdvanceBits); + nCodeBits = (U16)std::max((*cursor1)->MinCodeBits(), (U32)nCodeBits); + } + body.WriteByte((U32)nCodeBits); + body.WriteByte((U32)nAdvanceBits); + + std::list::iterator cursor; + for (cursor = textRecords.begin(); cursor != textRecords.end(); cursor++) { + (*cursor)->WriteToSWFStream(&body, nCodeBits, nAdvanceBits); + } + + body.FlushBits(); + body.WriteByte((U32)0); + + _SWFStream->AppendTag(stagDefineText, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FDTDefineText2 -------------------------------------------------------- + +FDTDefineText2::FDTDefineText2(FRect *_textBounds, FMatrix *_textMatrix, int glyhpsInFont) +{ + characterID = FObjCollection::Increment(); + textBounds = _textBounds; + textMatrix = _textMatrix; + nIndexBits = (int)FSWFStream::MinBits(glyhpsInFont, 0); +} + +FDTDefineText2::~FDTDefineText2() +{ + + delete textBounds; + + delete textMatrix; + + while (!textRecords.empty()) { + + delete textRecords.front(); + + textRecords.pop_front(); + } +} + +void FDTDefineText2::AddTextRecord(FTextRecord *_textRec) +{ + + textRecords.push_back(_textRec); +} + +U16 FDTDefineText2::ID(void) +{ + + return (U16)characterID; +} + +void FDTDefineText2::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + U16 nAdvanceBits = 0; + U16 nCodeBits = 0; + FSWFStream body; + + body.WriteWord((U32)characterID); + + textBounds->WriteToSWFStream(&body); + + textMatrix->WriteToSWFStream(&body); + + // Get the bits needed to write the advance and character codes + std::list::iterator cursor1; + for (cursor1 = textRecords.begin(); cursor1 != textRecords.end(); cursor1++) { + nAdvanceBits = (U16)std::max((*cursor1)->MinAdvanceBits(), (U32)nAdvanceBits); + nCodeBits = (U16)std::max((*cursor1)->MinCodeBits(), (U32)nCodeBits); + } + + body.WriteByte((U32)nCodeBits); + body.WriteByte((U32)nAdvanceBits); + + std::list::iterator cursor; + for (cursor = textRecords.begin(); cursor != textRecords.end(); cursor++) { + (*cursor)->WriteToSWFStream(&body, nCodeBits, nAdvanceBits); + } + + body.FlushBits(); + + body.WriteByte((U32)0); + + _SWFStream->AppendTag(stagDefineText2, body.Size(), &body); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FTextChangeRecord ----------------------------------------------------- + +FTextChangeRecord::FTextChangeRecord(U16 _hasFontFlag, U16 _hasColorFlag, + U16 _hasXOffsetFlag, U16 _hasYOffsetFlag, + U16 _fontID, U16 _fontHeight, FColor *_fontColor, + S16 _xOffset, S16 _yOffset) +{ + hasFontFlag = _hasFontFlag; + hasColorFlag = _hasColorFlag; + hasXOffsetFlag = _hasXOffsetFlag; + hasYOffsetFlag = _hasYOffsetFlag; + fontID = _fontID; + fontColor = _fontColor; + xOffset = _xOffset; + yOffset = _yOffset; + fontHeight = _fontHeight; +} + +FTextChangeRecord::~FTextChangeRecord() +{ + delete fontColor; +} + +// void FTextChangeRecord::IncludeNBitInfo(U16 _nIndexBits, U16 _nAdvanceBits){ +// Does absolutely nothing +// TextGlyphRecord needs this method +// Had to be virtual so it could be called on any TextRecord +// } + +void FTextChangeRecord::WriteToSWFStream(FSWFStream *_SWFStream, int /*codeBits*/, int /*advanceBits */) +{ + + _SWFStream->WriteBits((U32)1, 1); + + // _SWFStream->WriteBits((U32) reserved, 3); + _SWFStream->WriteBits((U32)0, 3); + + _SWFStream->WriteBits((U32)hasFontFlag, 1); + _SWFStream->WriteBits((U32)hasColorFlag, 1); + _SWFStream->WriteBits((U32)hasYOffsetFlag, 1); + _SWFStream->WriteBits((U32)hasXOffsetFlag, 1); + + if (hasFontFlag) + _SWFStream->WriteWord((U32)fontID); + + if (hasColorFlag) { // here changed. + fontColor->AlphaChannel(true); + fontColor->WriteToSWFStream(_SWFStream); + } + + if (hasXOffsetFlag) + _SWFStream->WriteWord((U32)xOffset); + + if (hasYOffsetFlag) + _SWFStream->WriteWord((U32)yOffset); + + if (hasFontFlag) + _SWFStream->WriteWord((U32)fontHeight); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FTextGlyphRecord ------------------------------------------------------ + +FTextGlyphRecord::FTextGlyphRecord() +{ +} + +FTextGlyphRecord::~FTextGlyphRecord() +{ + while (!glyphEntries.empty()) { + glyphEntries.pop_front(); + } +} + +void FTextGlyphRecord::AddGlyphEntry(U16 code, U16 advance) +{ + GlyphEntry glyph = {code, advance}; + + glyphEntries.push_back(glyph); +} + +U32 FTextGlyphRecord::MinAdvanceBits() +{ + std::list::iterator it; + + int maxBit = 0; + for (it = glyphEntries.begin(); it != glyphEntries.end(); ++it) { + maxBit = (int)std::max(FSWFStream::MinBits(it->advance, 1), (U32)maxBit); + } + return maxBit; +} + +U32 FTextGlyphRecord::MinCodeBits() +{ + // return FSWFStream::MinBits( glyphEntries.size(), 0 ); + std::list::iterator it; + + int maxBit = 0; + for (it = glyphEntries.begin(); it != glyphEntries.end(); ++it) { + int code = it->code; + maxBit = (int)std::max(FSWFStream::MinBits(code, 1), (U32)maxBit); + } + return maxBit; +} + +void FTextGlyphRecord::WriteToSWFStream(FSWFStream *_SWFStream, int codeBits, int advanceBits) +{ + FLASHASSERT((int)MinAdvanceBits() <= advanceBits); + FLASHASSERT((int)MinCodeBits() <= codeBits); + + // Bug: The number of glyphs used to limit the the max code size, but this changes with + // DefineFont2, where we could use glyph 38 as our sole glyph. So the re-write begins.... + int numberOfGlyphs = glyphEntries.size(); + + _SWFStream->WriteBits((U32)0, 1); + _SWFStream->WriteBits((U32)numberOfGlyphs, 7); + + std::list::iterator cursor; + + for (cursor = glyphEntries.begin(); cursor != glyphEntries.end(); cursor++) { + _SWFStream->WriteBits((U32)cursor->code, codeBits); + _SWFStream->WriteBits((U32)cursor->advance, advanceBits); + } +} diff --git a/toonz/sources/common/flash/FDTText.h b/toonz/sources/common/flash/FDTText.h new file mode 100644 index 0000000..b9a7263 --- /dev/null +++ b/toonz/sources/common/flash/FDTText.h @@ -0,0 +1,202 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FDTText.h + + This header-file contains the declarations of low-level text-related classes. + All parent classes are in the parentheses: + + class FTextRecord; + class FDTDefineEditText; (public FDT) + class FDTDefineText; (public FDT) + class FDTDefineText2; (public FDT) + class FTextChangeRecord; (public FTextRecord) + class FTextGlyphRecord; (public FTextRecord) + +****************************************************************************************/ + +#ifndef _F_DEFINE_TEXT_H_ +#define _F_DEFINE_TEXT_H_ + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "tcommon.h" +#include "Macromedia.h" +#include "FDT.h" +#include "FSWFStream.h" +#include "FPrimitive.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class FGlyphEntry; + +class DVAPI FTextRecord +{ +public: + virtual U32 MinAdvanceBits() = 0; // the computed minimum # of bits to record the advance values + virtual U32 MinCodeBits() = 0; // the computed minimum # of bits to record the character code values + virtual ~FTextRecord() {} + + // Because of multiple text records, the DefineText will specify the code and advance bits + // when it writes. + virtual void WriteToSWFStream(FSWFStream *_SWFStream, int codeBits, int advanceBits) = 0; +}; + +// A flash object that defines a font's appearance + +class DVAPI FDTDefineEditText : public FDT +{ + +public: + FDTDefineEditText(FRect *_bounds, U8 _hasFont, U8 _hasMaxLength, U8 _hasTextColor, + U8 _readOnly, U8 _password, U8 _multiline, U8 _wordWrap, U8 _hasText, + U8 _useOutlines, U8 _border, U8 _noSelect, U8 _hasLayout, U16 _fontID, + U16 _fontHeight, FColor *_textColor, U16 _maxLength, U8 _alignment, + U16 _leftMargin, U16 _rightMargin, U16 _indent, U16 _leading, + FString *_variableName, FString *_initialText); + virtual ~FDTDefineEditText(void); + U16 ID(void) { return characterID; } + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + FRect *bounds; + U8 hasFont; + U8 hasMaxLength; + U8 hasTextColor; + U8 readOnly; + U8 password; + U8 multiline; + U8 wordWrap; + U8 hasText; + U8 useOutlines; + U8 border; + U8 noSelect; + U8 hasLayout; + U16 fontID; + U16 fontHeight; + FColor *textColor; + U16 maxLength; + U8 alignment; + U16 leftMargin; + U16 rightMargin; + U16 indent; + U16 leading; + FString *variableName; + FString *initialText; + U16 characterID; +}; + +// A flash object that defines the font and formating of text characters in the record (flash 1.0) +// takes only RGB color values + +class DVAPI FDTDefineText : public FDT +{ + +public: + FDTDefineText(FRect *_textBounds, + FMatrix *_textMatrix, + int glyhpsInFont); // glyhpsInFont: how many characters are in the font? + virtual ~FDTDefineText(); + void AddTextRecord(FTextRecord *_textRec); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 characterID; + FRect *textBounds; + FMatrix *textMatrix; + std::list textRecords; + U16 nIndexBits; +}; + +// A flash object that defines the font and formating of text characters in the record (flash 1.0) +// takes RGBA color values + +class DVAPI FDTDefineText2 : public FDT +{ + +public: + FDTDefineText2(FRect *_textBounds, FMatrix *_textMatrix, + int glyhpsInFont); + virtual ~FDTDefineText2(); + void AddTextRecord(FTextRecord *_textRec); + U16 ID(void); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 characterID; + FRect *textBounds; + FMatrix *textMatrix; + std::list textRecords; + U16 nIndexBits; +}; + +// specifies text format changes in a flash DefineText object + +class DVAPI FTextChangeRecord : public FTextRecord +{ + +public: + FTextChangeRecord(U16 _hasFontFlag, U16 _hasColorFlag, + U16 _hasXOffsetFlag, U16 _hasYOffsetFlag, + U16 _fontID, U16 _fontHeight, FColor *_fontColor, + S16 _xOffset, S16 _yOffset); + virtual ~FTextChangeRecord(); + + virtual U32 MinAdvanceBits() { return 0; } + virtual U32 MinCodeBits() { return 0; } + + virtual void WriteToSWFStream(FSWFStream *_SWFStream, int advanceBits, int codeBits); + +private: + U16 reserved; + U16 hasFontFlag; + U16 hasColorFlag; + U16 hasYOffsetFlag; + U16 hasXOffsetFlag; + U16 fontID; + FColor *fontColor; + S16 xOffset; + S16 yOffset; + U16 fontHeight; +}; + +class DVAPI FTextGlyphRecord : public FTextRecord +{ + +public: + FTextGlyphRecord(); + virtual ~FTextGlyphRecord(); + + void AddGlyphEntry(U16 code, U16 advance); + virtual void WriteToSWFStream(FSWFStream *_SWFStream, int advanceBits, int codeBits); + + virtual U32 MinAdvanceBits(); // number of bits needed to write the advance data + virtual U32 MinCodeBits(); // number of bits needed to write the character code data + +private: + struct GlyphEntry { + U16 code; + U16 advance; + }; + std::list glyphEntries; +}; + +#endif diff --git a/toonz/sources/common/flash/FFixed.h b/toonz/sources/common/flash/FFixed.h new file mode 100644 index 0000000..b95489b --- /dev/null +++ b/toonz/sources/common/flash/FFixed.h @@ -0,0 +1,56 @@ +#ifndef FIXED_INCLUDED +#define FIXED_INCLUDED + +#define fixed_1 0x00010000L + +// fixed 2.0 +#define fixed2 0x00020000L +// fixed 0.5 +#define fixedHalf 0x00008000L +#define infinity 0x7FFFFFFFL +#define negInfinity 0x80000000L +#define fixedStdErr 0x0000003FL +// fixed sqrt(2) +#define fixedSqrt2 0x00016A0AL + +#define FixedRound(a) ((S16)((SFIXED)(a) + 0x8000L >> 16)) +#define FixedTrunc(a) ((S16)((SFIXED)(a) >> 16)) + +#define FixedCeiling(a) ((S16)(((SFIXED)(a) + 0x8000L) >> 16)) +#define FixedFloor(a) ((S16)((SFIXED)(a) >> 16)) + +#define FixedToInt(a) ((S16)((SFIXED)(a) + 0x8000L >> 16)) +#define IntToFixed(a) ((SFIXED)(a) << 16) +// Fixed integer constant +#define FC(a) IntToFixed(a) + +#define FixedToFloat(a) ((float)(a) / fixed_1) +#define FloatToFixed(a) ((SFIXED)((float)(a)*fixed_1)) + +#define FixedToDouble(a) ((double)(a) / fixed_1) +#define DoubleToFixed(a) ((SFIXED)((double)(a)*fixed_1)) + +#define FixedAverage(a, b) (((a) + (b)) >> 1) + +#define FixedAbs(x) ((x) < 0 ? -(x) : (x)) +#define FixedMin(a, b) ((a) < (b) ? (a) : (b)) +#define FixedMax(a, b) ((a) > (b) ? (a) : (b)) +#define FixedEqual(a, b, err) (FixedAbs((a) - (b)) <= err) + +SFIXED FixedNearestMultiple(SFIXED x, SFIXED factor); + +// Note that all angles are handled in Fixed point degrees to simplify rounding issues +// they are kept in the range of 0 to 360 degrees + +// Generic Floating point routines for quick porting not fast enough for shipping code +SFIXED FixedMul(SFIXED, SFIXED); +SFIXED FixedDiv(SFIXED, SFIXED); +SFIXED FixedSin(SFIXED); +SFIXED FixedCos(SFIXED); +SFIXED FixedTan(SFIXED); +SFIXED FixedAtan2(SFIXED dy, SFIXED dx); + +int _FPMul(int a, int b, int shift); +int _FPDiv(int a, int b, int rshift); + +#endif diff --git a/toonz/sources/common/flash/FObj.cpp b/toonz/sources/common/flash/FObj.cpp new file mode 100644 index 0000000..a099a5e --- /dev/null +++ b/toonz/sources/common/flash/FObj.cpp @@ -0,0 +1,246 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FObj.cpp + + This source file contains the definition for all low-level FObj-related functions, + grouped by classes: + + Class Member Function + + FObj U32 IsShowFrame(); + + FObjCollection FObjCollection(); + ~FObjCollection(); + void AddFObj(FObj*); + void WriteToSWFStream(FSWFStream*); + void CreateMovie(char*, SCOORD, SCOORD, U32); + void WriteFileHeader(U32, SCOORD, SCOORD, U32, FILE*); + void WriteEndTag(FSWFStream*); + U16 Increment(); + + FFragment FFragment(void*, int); + void WriteToSWFStream(FSWFStream*); + +****************************************************************************************/ + +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#include "FPrimitive.h" +#include "FObj.h" +#include "FSWFStream.h" + +//#include "tfilepath_io.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FObj ------------------------------------------------------------------ + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FObjCollection -------------------------------------------------------- + +// When constructor is called, the empty FObjList is automatically created. +FObjCollection::FObjCollection(void) +{ + + numOfFrames = 0; +} + +// Removes and deletes every element in the list. + +FObjCollection::~FObjCollection(void) +{ + + while (!FObjList.empty()) { + + delete FObjList.front(); + FObjList.pop_front(); + } +} + +// The given FObj is added to the end of FObjList + +void FObjCollection::AddFObj(FObj *fobj) +{ + + if (fobj->IsShowFrame()) //increment numFrames then store the tag + numOfFrames++; + + FObjList.push_back(fobj); +} + +void FObjCollection::EraseFObj(int tagindex) +{ + + std::list::iterator it = FObjList.begin(); + std::advance(it, tagindex); + FObjList.erase(it); +} + +void FObjCollection::InsertFObj(int beforeTag, FObj *fobj) +{ + + if (fobj->IsShowFrame()) //increment numFrames then store the tag + numOfFrames++; + + std::list::iterator it = FObjList.begin(); + std::advance(it, beforeTag); + FObjList.insert(it, fobj); +} + +int FObjCollection::GetFObjCount() const +{ + return FObjList.size(); +} + +FObj *FObjCollection::GetFObj(int i) const +{ + if (i >= (int)FObjList.size()) + return 0; + std::list::const_iterator it = FObjList.begin(); + std::advance(it, i); + + return *it; +} + +// Uses a cursor to loop through the entire list and write all of the +// list's FObjs to the given SWFStream + +void FObjCollection::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + std::list::iterator cursor; + + for (cursor = FObjList.begin(); cursor != FObjList.end(); cursor++) { + + (*cursor)->WriteToSWFStream(_SWFStream); + } +} + +void FObjCollection::CreateMovie(FILE *fp, FRect cameraBox, U32 _frameRate, U8 _version) +{ + //FILE* fp = fopen( fileName, "wb" ); + if (fp) { + FSWFStream stream; + + CreateMovie(&stream, cameraBox, _frameRate, _version); + stream.WriteToFile(fp); + } +} + +void FObjCollection::CreateCompressedMovie(FILE *fp, FRect cameraBox, U32 _frameRate) +{ + //FILE* fp = fopen( fileName, "wb" ); + if (fp) { + FSWFStream stream; + + CreateMovie(&stream, cameraBox, _frameRate); + stream.WriteToFileVersion6(fp); + } +} + +// creates a Flash Movie +void FObjCollection::CreateMovie(FSWFStream *swfStream, FRect cameraBox, U32 _frameRate, U8 _version) +{ + // Carve out space for the header -- always 21 bytes + for (int i = 0; i < HEADER_SIZE; i++) + swfStream->WriteByte(0); + + // This FObjCollection is dumped into swfFileBody + WriteToSWFStream(swfStream); + + //swfFileBody Terminated with a FCTEnd tag + WriteEndTag(swfStream); + + // write file header into file + WriteFileHeader(swfStream->Memory(), swfStream->Size(), cameraBox, _frameRate, _version); + + characterID = 0; +} + +// creates a Flash Movie +void FObjCollection::CreateSprite(FSWFStream *swfStream, FRect cameraBox, U32 /*_frameRate*/) +{ + // Sprites do not have headers. + + // This FObjCollection is dumped into swfFileBody + WriteToSWFStream(swfStream); + + //swfFileBody Terminated with a FCTEnd tag + WriteEndTag(swfStream); + + characterID = 0; +} + +// This works differently than the other calls, because it hacks the header information back +// into the beginning of an existing SWF stream. +void FObjCollection::WriteFileHeader(U8 *target, + U32 _fileLengthNoHeader, + FRect cameraBox, + U32 _frameRate, + U8 _version) +{ + FSWFStream header; + + header.WriteByte('F'); + header.WriteByte('W'); + header.WriteByte('S'); + header.WriteByte(_version); + + header.WriteDWord(_fileLengthNoHeader); + + // Write the movie dimensions "by hand" so they will have the correct bit size to fill the 21 bytes header + int nBits = 16; + header.WriteBits(nBits, 5); + + header.WriteBits((U32)cameraBox.Xmin(), nBits); + header.WriteBits((U32)cameraBox.Xmax(), nBits); + header.WriteBits((U32)cameraBox.Ymin(), nBits); + header.WriteBits((U32)cameraBox.Ymax(), nBits); + + header.FlushBits(); + + // The frame rate is 8.8 - we currently support 8.0 + header.WriteByte(0); + header.WriteByte(_frameRate); + + header.WriteWord(numOfFrames); + + // Copy this buffer to the target + memcpy(target, header.Memory(), HEADER_SIZE); +} + +void FObjCollection::WriteEndTag(FSWFStream *_SWFStream) +{ + + _SWFStream->AppendTag(stagEnd, 0, 0); +} + +// stores the number of flash objects created +U16 FObjCollection::characterID = 0; + +// increments the number of flash objects created +U16 FObjCollection::Increment(void) +{ + assert(characterID != 0xffff); + return ++characterID; +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FFragment ------------------------------------------------------------- + +FFragment::FFragment(const void *_data, int _size) +{ + data = _data; + size = _size; +} + +void FFragment::WriteToSWFStream(FSWFStream *_SWFStream) +{ + _SWFStream->WriteLargeData((U8 *)data, size); +} diff --git a/toonz/sources/common/flash/FObj.h b/toonz/sources/common/flash/FObj.h new file mode 100644 index 0000000..0baa804 --- /dev/null +++ b/toonz/sources/common/flash/FObj.h @@ -0,0 +1,120 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. +// Last Modified On 18/06/2002 by DV for Fixes. +/**************************************************************************************** + + File Summary: FObj.h + + This header-file contains the declarations of low-level FObj-related classes. + All parent classes are in the parentheses: + + class FObj; + class FFragment; (public FObj) + class FObjCollection; + +****************************************************************************************/ + +#ifndef _FOBJ_H_ +#define _FOBJ_H_ + +#include "tcommon.h" +#include +#include + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "Macromedia.h" +#include "FPrimitive.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +//All Flash tagged data block objects fall into this category + +class DVAPI FObj +{ +public: + virtual ~FObj() {} // Doesn't do anything, just makes all the other destructors virtual + + virtual void WriteToSWFStream(FSWFStream *_SWFStream) = 0; + virtual U32 IsShowFrame() { return false; } + virtual U32 IsPlaceObject() { return false; } + virtual bool IsDefineShape() { return false; } + virtual bool IsSprite() { return false; } + + virtual void SetId(U16 id) { FLASHASSERT(0); } + virtual void SetMatrix(FMatrix *matrix) { FLASHASSERT(0); } +}; + +/*! A class for writing an arbitrary block of data (a SWF fragment, perhaps) to + * the SWFStream. The data is assumed to be large, so it is not changed. + */ +class FFragment : public FObj +{ +public: + FFragment(const void *data, int size); + virtual void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + const void *data; + int size; +}; + +// Holds a collection of FObj's so that they can be dumped in a SWF format +//Writes tags in the same order as they appear in the collection + +//using namespace std; + +class DVAPI FObjCollection +{ + +public: + FObjCollection(void); + ~FObjCollection(void); + void AddFObj(FObj *fobj); + void InsertFObj(int beforeTag, FObj *fobj); + void EraseFObj(int tagindex); + + void WriteToSWFStream(FSWFStream *_SWFStream); + //void CreateCompressedMovie( const TFilePath &_fileName, FRect cameraBox, U32 _frameRate = 12 ); + //void CreateMovie( const TFilePath &_fileName, FRect cameraBox, U32 _frameRate = 12, U8 _version = 6); + void CreateCompressedMovie(FILE *fp, FRect cameraBox, U32 _frameRate = 12); + void CreateMovie(FILE *fp, FRect cameraBox, U32 _frameRate = 12, U8 _version = 6); + void CreateMovie(FSWFStream *stream, FRect cameraBox, U32 _frameRate = 12, U8 _version = 6); + void CreateSprite(FSWFStream *stream, FRect cameraBox, U32 _frameRate = 12); + static U16 Increment(void); + int GetFObjCount() const; + FObj *GetFObj(int i) const; + +private: + enum { + HEADER_SIZE = 21 + }; + + U32 numOfFrames; + std::list FObjList; + void WriteFileHeader(U8 *target, U32 _fileLengthNoHeader, FRect cameraBox, + U32 _frameRate, U8 _version); + void WriteEndTag(FSWFStream *_SWFStream); + static U16 characterID; +}; + +#ifdef WIN32 // added from DV +#pragma warning(pop) +#endif + +#endif diff --git a/toonz/sources/common/flash/FPrimitive.cpp b/toonz/sources/common/flash/FPrimitive.cpp new file mode 100644 index 0000000..f6505af --- /dev/null +++ b/toonz/sources/common/flash/FPrimitive.cpp @@ -0,0 +1,268 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FPrimitive.cpp + + This source file contains the definition for all low-level primitive-related functions, + grouped by classes: + + Class Member Function + + FColor FColor(U32, U32, U32); + FColor(U32, U32, U32, U32); + FColor(FRGB); + FColor(FRGBA); + void WriteToSWFStream(FSWFStream*, int); + + FMatrix FMatrix(U32, SFIXED, SFIXED, + U32, SFIXED, SFIXED, + SCOORD , SCOORD); + Matrix(); + U32 MinBits(S32, S32); + void WriteToSWFStream(FSWFStream*); + + FRect FRect(SCOORD, SCOORD, SCOORD, SCOORD); + TUINT32 MinBits(); + void WriteToSWFStream(FSWFStream *); + + FString FString(const U8*); + void WriteToSWFStream(FSWFStream*, bool); + +****************************************************************************************/ + +#include "FPrimitive.h" +#include "FSWFStream.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FColor ---------------------------------------------------------------- + +FColor::FColor(U32 _red, U32 _green, U32 _blue) + : red(_red), + green(_green), + blue(_blue), + alpha(0xff), + alphaT(false) +{ +} + +FColor::FColor(U32 _red, U32 _green, U32 _blue, U32 _alpha) + : red(_red), + green(_green), + blue(_blue), + alpha(_alpha), + alphaT(true) +{ +} + +FColor::FColor(FRGB rgb) + : red(rgb.red), + green(rgb.green), + blue(rgb.blue), + alpha(0xff), + alphaT(false) +{ +} + +FColor::FColor(FRGBA rgba) + : red(rgba.red), + green(rgba.green), + blue(rgba.blue), + alpha(rgba.alpha), + alphaT(true) +{ +} + +void FColor::WriteToSWFStream(FSWFStream *_SWFStream) +{ + //no filling, everything should already be byte aligned + _SWFStream->WriteByte(red); + _SWFStream->WriteByte(green); + _SWFStream->WriteByte(blue); + if (alphaT) { + _SWFStream->WriteByte(alpha); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FMatrix --------------------------------------------------------------- + +// The Matrix class constructor +FMatrix::FMatrix(U32 _hasScale, SFIXED _scaleX, SFIXED _scaleY, U32 _hasRotate, SFIXED + _rotateSkew0, + SFIXED _rotateSkew1, SCOORD _translateX, SCOORD _translateY) +{ + + hasScale = _hasScale; + + scaleX = _scaleX; + scaleY = _scaleY; + + hasRotate = _hasRotate; + rotateSkew0 = _rotateSkew0; + rotateSkew1 = _rotateSkew1; + + translateX = _translateX; + translateY = _translateY; + + nScaleBits = MinBits(scaleX, scaleY); + nRotateBits = MinBits(rotateSkew0, rotateSkew1); + nTranslateBits = MinBits(translateX, translateY); + + identity = false; +} + +// Creates an identity matrix. + +FMatrix::FMatrix(void) +{ + + identity = true; + hasScale = false; + scaleX = 1; + scaleY = 1; + + hasRotate = false; + rotateSkew0 = 0; + rotateSkew1 = 0; +} + +// Calculates the minimum number of bits necessary to represent the given 2 signed numbers. +// This is used to calculate the 3 nbit fields in the Matrix class. Takes two signed +// numbers, sees which has the greatest magnitude, and calls FileWrite::MinBits with the +// unsigned magnitude of the larger number and the sign flag equal to 1 to account for the +// fact that the numbers are signed. + +U32 FMatrix::MinBits(S32 x, S32 y) +{ + + int xAbs = abs(x); + int yAbs = abs(y); + + if (xAbs > yAbs) + return FSWFStream::MinBits((U32)xAbs, 1); + + else + return FSWFStream::MinBits((U32)yAbs, 1); +} + +FMatrix FMatrix::operator*(const FMatrix &b) const +{ + SFIXED _scaleX = scaleX * b.scaleX + rotateSkew0 * b.rotateSkew1; + SFIXED _scaleY = scaleY * b.scaleY + rotateSkew0 * b.rotateSkew1; + SFIXED _rot0 = scaleX * b.rotateSkew0 + rotateSkew0 * b.scaleY; + SFIXED _rot1 = rotateSkew1 * b.scaleX + scaleY * b.rotateSkew1; + + return FMatrix( + _scaleX != 1 || scaleY != 1, _scaleX, _scaleY, + _rot0 != 0 || _rot1 != 0, _rot0, _rot1, + scaleX * b.translateX + rotateSkew0 * b.translateY + translateX, + rotateSkew1 * b.translateX + scaleY * b.translateY + translateY); +} + +// Writes the Matrix to the given _SWFStream. + +void FMatrix::WriteToSWFStream(FSWFStream *_SWFStream) +{ + + if (identity) { + + _SWFStream->WriteByte(0); + + } + + else { + _SWFStream->FlushBits(); + + _SWFStream->WriteBits(hasScale, 1); + + if (hasScale) { + + _SWFStream->WriteBits(nScaleBits, 5); + _SWFStream->WriteBits((U32)scaleX, nScaleBits); + _SWFStream->WriteBits((U32)scaleY, nScaleBits); + } + + _SWFStream->WriteBits(hasRotate, 1); + + if (hasRotate) { + + _SWFStream->WriteBits(nRotateBits, 5); + _SWFStream->WriteBits((U32)rotateSkew0, nRotateBits); + _SWFStream->WriteBits((U32)rotateSkew1, nRotateBits); + } + if (translateX == 0 && translateY == 0) + _SWFStream->WriteBits(0, 5); + else { + _SWFStream->WriteBits(nTranslateBits, 5); + + _SWFStream->WriteBits((U32)translateX, nTranslateBits); + _SWFStream->WriteBits((U32)translateY, nTranslateBits); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FRect ------------------------------------------------------------------ + +// Rectangle class constructor. +FRect::FRect(SCOORD _xmin, SCOORD _ymin, SCOORD _xmax, SCOORD _ymax) +{ + + xmin = _xmin; + xmax = _xmax; + ymin = _ymin; + ymax = _ymax; +} + +// FRect's MinBits function. Calls MinBits with number being the absolute value of the +// rectangle coordinate with the greatest magnitude, and sign being 1 since a rectangle +// coord is signed. + +TUINT32 FRect::MinBits(void) +{ + TUINT32 maxCoord = FSWFStream::MaxNum(xmin, xmax, ymin, ymax); + + return FSWFStream::MinBits(maxCoord, 1); +} + +// Writes the rectangle to the given buffer. + +void FRect::WriteToSWFStream(FSWFStream *_SWFStream) +{ + int nBits = (int)MinBits(); + _SWFStream->WriteBits(nBits, 5); + + _SWFStream->WriteBits((U32)xmin, nBits); + _SWFStream->WriteBits((U32)xmax, nBits); + _SWFStream->WriteBits((U32)ymin, nBits); + _SWFStream->WriteBits((U32)ymax, nBits); + + _SWFStream->FlushBits(); +} + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FString --------------------------------------------------------------- + +FString::FString(const U8 *_string) +{ + text = (const char *)_string; +} + +FString::FString(const char *_string) +{ + text = _string; +} + +void FString::WriteToSWFStream(FSWFStream *_SWFStream, bool null_end) +{ + U16 i = 0; + for (i = 0; i < text.length(); i++) { + _SWFStream->WriteByte((U32)text[i]); + } + if (null_end) + _SWFStream->WriteByte(0); +} diff --git a/toonz/sources/common/flash/FPrimitive.h b/toonz/sources/common/flash/FPrimitive.h new file mode 100644 index 0000000..4cd2b69 --- /dev/null +++ b/toonz/sources/common/flash/FPrimitive.h @@ -0,0 +1,235 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/18/1999. +// Mostly about FRGB, FRGBA, and FColor. +// Additionally, there is some brain-dump on how color should be implemented in low and +// high level manager. + +/**************************************************************************************** + + File Summary: FPrmitive.h + + This header-file contains the declarations of low-level prmitive-related structs and + classes. + + struct FRGB; + struct FRGBA; + + class FColor; + class FMatrix; + class FRect; + class FString; + +****************************************************************************************/ + +#ifndef FPRIMITIVE_INCLUDED +#define FPRIMITIVE_INCLUDED +#include "tcommon.h" +#include "Macromedia.h" +#include +class FSWFStream; + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +// color structures + +struct FRGB { + U8 red; + U8 green; + U8 blue; +}; + +struct FRGBA { + // FRGBA( U8 r, U8 g, U8 b, U8 a ) { red = r; green = g; blue = b; alpha = a; } + // FRGBA() { red = 0; green = 0; blue = 0; alpha = 255; } + // void Set( U8 r, U8 g, U8 b, U8 a ) { red = r; green = g; blue = b; alpha = a; } + // + U8 red; + U8 green; + U8 blue; + U8 alpha; +}; + +//! This object specifies a color object. +/*! FColor is used (copied) as a structure. Do not add dynamic memory or virtual functions. + + There are two types of FColor, i.e. one with alpha channel on and the other with alpha channel.off. + When you construct an instance of FColor, if you provide three parameters, the alpha channel is off; + if you provide four, the alpha channel is on. + + Each color-related SWF tag requires the correct color type, either RGB or RGBA. For example, + DefineShape, DefineShape2, SetBackgroundColor need RGB (alpha off); + DefineShape3, DefineMorphShape, DefineEditText need RGBA (alpha on). + + The low-level manager can correct some errors of wrong color type, but not all. So don't rely on it. + If the wrong one got over low-level manager, the flash play might crash on that. + + We recommend you use RGBA whenever reasonable. The reason? Using RGBA is very consistent + and worth the sacrifice of some file space (in SWF, it's one more byte for each occurance of color info, and some + additional for color transformation.) + + Actually, we only provide RGBA interface for colors in high-level manager because this solution is much more elegant + in terms of consistency and compatibility. +*/ +class DVAPI FColor +{ +public: + //! Default FColor is white (0xff, 0xff, 0xff), construct a FColor with Alpha channel off. + FColor(U32 _red = 0xff, U32 _green = 0xff, U32 _blue = 0xff); + + //! Construct a FColor with Alpha channel on. + FColor(U32 _red, U32 _green, U32 _blue, U32 _alpha); + + //! Construct a FColor with Alpha channel off. + FColor(FRGB rgb); + + //! Construct a FColor with Alpha channel on. + FColor(FRGBA rgba); + + //! Returns red component. + int Red() { return (int)red; } + + //! Returns green component. + int Green() { return (int)green; } + + //! Returns blue component. + int Blue() { return (int)blue; } + + //! Returns alpha value. + int Alpha() { return (int)alpha; } + + bool HasAlpha() { return alphaT; } + + //! Alpha switch: turn on/off alpha channel. + void AlphaChannel(bool on) { alphaT = on; } + + bool operator==(const FColor &color) { return (red == color.red && + green == color.green && + blue == color.blue && + alpha == color.alpha); } + + /* + enum { + WRITE_SMALLEST, + WRITE_3BYTE, + WRITE_4BYTE + }; +*/ + // Method for internal use. + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + U32 red; // should be U8 + U32 green; // should be U8 + U32 blue; // should be U8 + U32 alpha; // should be U8 + bool alphaT; //alpha type flag +}; + +class DVAPI FMatrix +{ + +public: + FMatrix(void); + + FMatrix(U32 _hasScale, + SFIXED _scaleX, + SFIXED _scaleY, + U32 _hasRotate, + SFIXED _rotateSkew0, + SFIXED _rotateSkew1, + SCOORD _translateX, + SCOORD _translateY); + + void WriteToSWFStream(FSWFStream *_SWFStream); + + FMatrix operator*(const FMatrix &b) const; + +public: + U32 hasScale; + U32 nScaleBits; + + SFIXED scaleX; + SFIXED scaleY; + + U32 hasRotate; + U32 nRotateBits; + SFIXED rotateSkew0; + SFIXED rotateSkew1; + + U32 nTranslateBits; + SCOORD translateX; + SCOORD translateY; + + U32 MinBits(S32 x, S32 y); + U16 identity; +}; + +//rectangle class + +class DVAPI FRect +{ +public: + FRect() { xmin = ymin = xmax = ymax = 0; } + FRect(SCOORD xmin, SCOORD ymin, SCOORD xmax, SCOORD ymax); + + SCOORD Xmin() { return xmin; } + SCOORD Ymin() { return ymin; } + SCOORD Xmax() { return xmax; } + SCOORD Ymax() { return ymax; } + SCOORD Width() { return xmax - xmin + 1; } + SCOORD Height() { return ymax - ymin + 1; } + + void SetXmin(SCOORD val) { xmin = val; } + void SetYmin(SCOORD val) { ymin = val; } + void SetXmax(SCOORD val) { xmax = val; } + void SetYmax(SCOORD val) { ymax = val; } + + void WriteToSWFStream(FSWFStream *_SWFStream); + +private: + SCOORD xmin; + SCOORD xmax; + SCOORD ymin; + SCOORD ymax; + + U32 MinBits(void); +}; + +// a flash string + +class DVAPI FString +{ + +public: + FString(const U8 *_string); + FString(const char *_string); + std::string GetString() { return text; } + + void WriteToSWFStream(FSWFStream *_SWFStream, bool writeNull); + U16 Length() { return text.length(); } + +private: + std::string text; +}; + +#ifdef WIN32 // added from DV +#pragma warning(pop) +#endif +#endif diff --git a/toonz/sources/common/flash/FSWFStream.cpp b/toonz/sources/common/flash/FSWFStream.cpp new file mode 100644 index 0000000..b8a15e6 --- /dev/null +++ b/toonz/sources/common/flash/FSWFStream.cpp @@ -0,0 +1,494 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FSWFStream.cpp + + This source file contains the definition for all low-level SWF-stream functions: + + Class Member Function + + FSWFStream FSWFStream(); + void WriteBits(U32, U32); + void WriteLargeData(U8*, U32); + void FlushBits(); + void WriteDWord(U32); + void WriteWord(U32); + void WriteByte(U32); + U32 Size(); + void Append(FSWFStream *); + void WriteToFile( FILE*); + void AppendTag(U16, U32, FSWFStream*); + U32 MinBits(U32, U16); + U32 MaxNum(S32, S32, S32, S32); + +****************************************************************************************/ +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#ifndef _DEBUG +#undef _STLP_DEBUG +#else +#define _STLP_DEBUG 1 +#endif + +#include "zlib.h" +#include "FObj.h" +#include "FSWFStream.h" + +////////////////////////////////////////////////////////////////////////////////////// +// -------- FSWFStream ------------------------------------------------------------ + +// FSWFStream is the object used to store a string of bytes, an SWF file "in memory" +// before being written to the disk. It handles bit packing and endian issues. + +// SWF uses bit packing a lot to reduce the size of the data going over the Net. +// The issue here of course is how to pack and unpack the bits correctly. + +FSWFStream::FSWFStream() +{ + streamPos = 0; //the position in the FSWFStream(how many bytes you have put in) + bytePos = 8; //the number of bits left to fill in the current byte + currentByte = 0; //the value of the current byte being created + +#ifndef DEBUG + stream.reserve(1024); // start out at 1k to make faster - + // if it is a debug build, don't reserve, as we wish to stress the system +#endif + stream.push_back(0); +} + +// Adds 'size' bits from 'data' to the stream FSWFStream. Data is in the form of +// a U32. Size indicates how many of the 32 bits are significant and should +// be output. It checks how many bits are available in the current output byte +// and works by repeatedly stuffing it with the next bits from 'data' +// and then adding currentByte to the stream until all "size" bits have been output. + +void FSWFStream::WriteBits(U32 data, U32 size) //adds individual bits +{ + FLASHASSERT(((int)data <= (0x01 << size) - 1) || (-(S32)(data) <= (0x01 << size) - 1)); + while (size != 0) { + if (size > bytePos) { + //if more bits left to write than shift out what will fit + currentByte |= data << (32 - size) >> (32 - bytePos); + + // shift all the way left, then right to right + // justify the data to be or'ed in + stream[streamPos] = currentByte; + streamPos++; + stream.push_back(0); + size -= bytePos; + currentByte = 0; + bytePos = 8; + } else if (size <= bytePos) { + currentByte |= data << (32 - size) >> (32 - bytePos); + bytePos -= size; + size = 0; + + if (!bytePos) { //if current byte is filled + stream[streamPos] = currentByte; + streamPos++; + stream.push_back(0); + currentByte = 0; + bytePos = 8; + } + } + } +} + +// For adding large data that is pointed to by a pointer. The data is only +// integrated into the stream when it is actually written to disk. +// E. G. a large JPEG. This is to avoid storing it twice. +// Stores the current streamPos, data pointer and size in the OutDataList. + +void FSWFStream::WriteLargeData(const U8 *data, U32 size) +{ + LargeData large; + + large.position = streamPos; + large.data = data; + large.size = size; + + outDataList.push_back(large); +} + +// Kick out the current partially filled byte to the stream. +// If there is a byte currently being built for addition to the stream, then the end of that +// byte is filled with zeroes and the byte is added to the stream. + +void FSWFStream::FlushBits() +{ + + if (bytePos != 8) { + + stream[streamPos] = currentByte; + streamPos++; + stream.push_back(0); + currentByte = 0; + bytePos = 8; + } +} + +// + +// Writes a 32 bit stream of data to given FSWFStream in the proper form (reversed byte order), +// so B1B2B3B4 is written as B4B3B2B1. The function does this by sending a byte at a time +// of the data to the FSWFStream in the appropriate order. + +void FSWFStream::WriteDWord(U32 data) +{ + //declare variable used to output the bytes + U32 v; + + FlushBits(); //vincenzo: l'ho messo io!! col cazzo che era "no bit swapping"! stronzi! + + //output the rightmost byte + v = data << 24 >> 24; + WriteBits(v, 8); + + //output the center right byte + v = data << 16 >> 24; + WriteBits(v, 8); + + //output the center left byte + v = data << 8 >> 24; + WriteBits(v, 8); + + //output the leftmost byte + v = data >> 24; + WriteBits(v, 8); +} + +// Writes a 16 bit stream of data to the FSWFStream in the proper form, so B1B2 is written as +// B2B1. + +void FSWFStream::WriteWord(U32 data) +{ + + //declare the variable used to output the bytes + U32 v; + FlushBits(); //vincenzo: l'ho messo io!! col cazzo che era "no bit swapping"! stronzi! + + //output the rightmost byte + v = data << 24 >> 24; + WriteBits(v, 8); + + //output the leftmost byte + v = data << 16 >> 24; + WriteBits(v, 8); +} + +// Writes an 8 bit stream of data to the FSWFStream. There is no bit swapping!! A byte is +// written as a byte. + +void FSWFStream::WriteByte(U32 data) +{ + + //declare the variable used to output the byte + U32 v = 0; + FlushBits(); //vincenzo: l'ho messo io!! col cazzo che era "no bit swapping"! stronzi! + + //output the byte + v = data << 24 >> 24; + + WriteBits(v, 8); +} + +// Returns the size of the FSWFStream. For purposes of denoting size in tags and headers. + +U32 FSWFStream::Size(void) +{ + int size = (int)streamPos; + std::list::iterator it; + + for (it = outDataList.begin(); it != outDataList.end(); it++) { + LargeData &data = (*it); + size += data.size; + } + return size; +} + +//------------------------------------------------------------------ +U32 FSWFStream::FullSize(void) +{ + U32 currentStreamPos = 0; //the current position in the FSWFStream for writing + + std::list::iterator it = outDataList.begin(), it_e = outDataList.end(); + U32 size = 0; + + for (; it != it_e; it++) { + + FLASHASSERT((*it).position >= currentStreamPos); + + if ((*it).position - currentStreamPos > 0) + size += (*it).position - currentStreamPos; + + currentStreamPos = (*it).position; + + //currentStreamPos should now equal currentDataPosition + FLASHASSERT((*it).size > 0); + + size += (*it).size; + } + + if (streamPos > currentStreamPos) + size += streamPos - currentStreamPos; + return size; +} + +//------------------------------------------------------------------- +// Appends the stream FSWFStream to this. Doesn't actually write the bitmaps, +// jpegs ... Instead it just writes their file name with a note that the actual file +// should go there. + +void FSWFStream::Append(FSWFStream *add) +{ + int addStreamPos = 0; // this functions position in the "add" stream, + // remembering that add->streamPos is the END + // of the "add" stream. + + // remove all the large data from the other stream + while (add->outDataList.size()) { + LargeData data = add->outDataList.front(); + add->outDataList.pop_front(); + + for (; addStreamPos < (int)data.position; addStreamPos++) { + WriteBits(add->stream[addStreamPos], 8); + } + //addStreamPos should now equal data.position + WriteLargeData(data.data, data.size); + } + + // Write the remainder of the stream data, after the last outData. + for (; addStreamPos < (int)add->streamPos; addStreamPos++) { + WriteBits(add->stream[addStreamPos], 8); + } +} + +// Writes the stream FSWFStream to the given file. +//--------------------------------------------------------------------- + +void FSWFStream::WriteToFileVersion6(FILE *swfFile) +{ + U32 size = FullSize(); + U8 *buf = new U8[size]; + WriteToMemory(buf); + + assert(buf[0] == 'F'); + assert(buf[1] == 'W'); + assert(buf[2] == 'S'); + //assert(buf[3]==4); + assert(((U32)(buf[4] + (buf[5] << 8) + (buf[6] << 16) + (buf[7] << 24))) == size); + + buf[3] = 6; //version 6 + + U32 sizeOut = (U32)(2 * ((size * 1.1) + 12)); + U8 *bufOut = new U8[sizeOut]; + + compress(bufOut, (uLongf *)&sizeOut, buf + 8, size - 8); + + if (size > sizeOut + 8) //meglio non comprimere altrimenti!!! + { + buf[0] = 'C'; + fwrite(buf, 1, 8, swfFile); + fwrite(bufOut, 1, sizeOut, swfFile); + } else + fwrite(buf, 1, size, swfFile); + + delete[] bufOut; + delete[] buf; +} + +//------------------------------------------------------------------------------------- + +void FSWFStream::WriteToFile(FILE *swfFile) +{ + U32 currentStreamPos = 0; //the current position in the FSWFStream for writing + const U8 *currentData; + U32 currentDataSize; + U32 currentDataPosition; + U32 outDataListSize = outDataList.size(); + + int wrote = 0; + + if (outDataListSize) { + + for (U32 i = 0; i < outDataListSize; i++) { + currentDataPosition = outDataList.front().position; + currentData = outDataList.front().data; + currentDataSize = outDataList.front().size; + outDataList.pop_front(); + + FLASHASSERT(currentDataPosition >= currentStreamPos); + + if (currentDataPosition - currentStreamPos > 0) { + fwrite(&stream[currentStreamPos], 1, (currentDataPosition - currentStreamPos), swfFile); + } + wrote += currentDataPosition - currentStreamPos; + currentStreamPos = currentDataPosition; + + //currentStreamPos should now equal currentDataPosition + FLASHASSERT(currentDataSize > 0); + + fwrite(currentData, 1, currentDataSize, swfFile); + wrote += currentDataSize; + } + } + + if (streamPos > currentStreamPos) { + fwrite(&stream[currentStreamPos], 1, streamPos - currentStreamPos, swfFile); + } + wrote += streamPos - currentStreamPos; +} + +void FSWFStream::WriteToMemory(U8 *memory) +{ + U32 currentStreamPos = 0; //the current position in the FSWFStream for writing + const U8 *currentData; + U32 currentDataSize; + U32 currentDataPosition; + U32 outDataListSize = outDataList.size(); + + int wrote = 0; + + if (outDataListSize) { + + for (U32 i = 0; i < outDataListSize; i++) { + currentDataPosition = outDataList.front().position; + currentData = outDataList.front().data; + currentDataSize = outDataList.front().size; + outDataList.pop_front(); + + FLASHASSERT(currentDataPosition >= currentStreamPos); + + if (currentDataPosition - currentStreamPos > 0) { + memcpy(memory, &stream[currentStreamPos], (currentDataPosition - currentStreamPos)); + memory += currentDataPosition - currentStreamPos; + } + wrote += currentDataPosition - currentStreamPos; + currentStreamPos = currentDataPosition; + + //currentStreamPos should now equal currentDataPosition + FLASHASSERT(currentDataSize > 0); + + memcpy(memory, currentData, currentDataSize); + memory += currentDataSize; + wrote += currentDataSize; + } + } + + if (streamPos > currentStreamPos) { + // fwrite( &stream[currentStreamPos], 1, streamPos - currentStreamPos, swfFile ); + memcpy(memory, &stream[currentStreamPos], streamPos - currentStreamPos); + memory += streamPos - currentStreamPos; + } + wrote += streamPos - currentStreamPos; +} + +void FSWFStream::AppendTag(U16 tagID, U32 length, FSWFStream *buffer) +{ + U32 longLength = 0; + bool longHead = false; + + if (length > 62 || (tagID == 32 && length == 29) || (tagID == 2 && length == 28) || (tagID == 26 && length == 19)) //If long type tag: + { + longHead = true; + longLength = length; //The actual length is here. + length = 0x3f; //This field's length becomes 63 to indicate a TLong tag. + } else { //Else short type tag: + longHead = false; //It is not a TLong header, so the length is valid. + } + + U16 firstPartOfTag = (U16)((tagID << 6) | length); // Build up the first 2 bytes of the tag: + // 10bits for tag ID + // 6bits for tag length + + WriteWord((U32)firstPartOfTag); + + if (longHead) { + WriteDWord(longLength); + } + + if (buffer) // If there is not a buffer, don't write any more. + { + Append(buffer); // Copy the buffer passed in to this object. + } +} + +// Calculates the minimum number of bits necessary to represent the given number. The +// number should be given in its unsigned form with the flag sign equal to 1 if it is +// signed. Repeatedly compares number to another unsigned int called x. +// x is initialized to 1. The value of x is shifted left i times until x is greater +// than number. Now i is equal to the number of bits the UNSIGNED value of number needs. +// The signed value will need one more bit for the sign so i+1 is returned if the number +// is signed, and i is returned if the number is unsigned. + +U32 FSWFStream::MinBits(U32 number, U16 sign) +{ + //If the number == 0, then 0 bits are necessary for unsigned, and 1 for signed. + //Sign should either have a value of 0 or 1. + if (number == 0) { + return sign; + } + + //declare and initialize the variable for comparison + U32 x = 1; + U32 i; + + //keep increasing the value of x and i until s is greater than the given number + for (i = 1; i < 33; i++) { + x <<= 1; + if (x > number) { + break; + } + } + + FLASHASSERT(sign + i <= 32); + + //return the calculated value and account for the number being signed or not + return i + sign; +} + +// Compares the absolute values of 4 signed integers and returns the unsigned magnitude of +// the number with the greatest absolute value. + +U32 FSWFStream::MaxNum(S32 a, S32 b, S32 c, S32 d) +{ + + //take the absolute values of the given numbers + int aAbs = abs(a); + int bAbs = abs(b); + int cAbs = abs(c); + int dAbs = abs(d); + + //compare the numbers and return the unsigned value of the one with the greatest magnitude + if (aAbs > bAbs) { + if (aAbs > cAbs) { + if (aAbs > dAbs) { + return (U32)aAbs; + } else { + return (U32)dAbs; + } + } else if (cAbs > dAbs) { + return (U32)cAbs; + } else { + return (U32)dAbs; + } + } else { + if (bAbs > cAbs) { + if (bAbs > dAbs) { + return (U32)bAbs; + } else { + return (U32)dAbs; + } + } else if (cAbs > dAbs) { + return (U32)cAbs; + } else { + return (U32)dAbs; + } + } +} diff --git a/toonz/sources/common/flash/FSWFStream.h b/toonz/sources/common/flash/FSWFStream.h new file mode 100644 index 0000000..eef08b1 --- /dev/null +++ b/toonz/sources/common/flash/FSWFStream.h @@ -0,0 +1,103 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. + +/**************************************************************************************** + + File Summary: FSWFStream.h + + This header-file contains the declarations of low-level SWF-stream class, i.e. + + class FSWFStream; + +****************************************************************************************/ + +#ifndef SWFSTREAM_INCLUDED +#define SWFSTREAM_INCLUDED + +#ifdef WIN32 // added from DV +#pragma warning(push) +#pragma warning(disable : 4786) +#pragma warning(disable : 4251) + +#endif + +#include "Macromedia.h" + +// #include +// #include +#include +#include + +#include "tcommon.h" + +#undef DVAPI +#undef DVVAR +#ifdef TFLASH_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +// class used to store data before it is written to a .swf file + +class DVAPI FSWFStream +{ +public: + FSWFStream(); + + U32 Size(); + U32 FullSize(); + + U8 *Memory() { return &stream[0]; } // Useful to get the origin of the stream memory. + // Used by CreateMovie to write the header information. + + void WriteBits(U32 data, U32 size); // Writes # of bits size, that has value data + void WriteLargeData(const U8 *data, U32 size); // Stores the pointer to a large block of data + // to be written. Ownership of the data is NOT passed + // to this stream. + + // NOTE: These functions all take in U32's even though in some situations a smaller data + // type can suffice. This is for the sake of simplicity. + void WriteDWord(U32 data); + void WriteWord(U32 data); + void WriteByte(U32 data); + + void FlushBits(); // Kick out the current partially filled byte to the stream. + + void AppendTag(U16 _tagID, U32 _length, FSWFStream *_tagBody); // Copies the stream, adding tag id + // and length information. + void Append(FSWFStream *_SWFStream); // Copies another stream to the end + // of this without any changes. The stream + // will be empty but still needs to be deleted + // by the caller. + + // Utility functions + void WriteToFile(FILE *out); // writes this data to file + void WriteToFileVersion6(FILE *out); // writes this data to file, compressing with zlib(flash 6) + void WriteToMemory(U8 *memory); // copy this data to a memory buffer (use Size() to make sure it is large enough) + + //functions used to calculate optimal size for fields + static U32 MinBits(U32 number, U16 sign); + static U32 MaxNum(S32 a, S32 b, S32 c, S32 d); + +private: + struct LargeData { + U32 position; + const U8 *data; + U32 size; + }; + + std::vector stream; + + U32 streamPos; + U32 bytePos; + U8 currentByte; + + std::list outDataList; // stores pointer data - the overhead is not as bad as it seems +}; + +#endif diff --git a/toonz/sources/common/flash/FSound.cpp b/toonz/sources/common/flash/FSound.cpp new file mode 100644 index 0000000..01e184a --- /dev/null +++ b/toonz/sources/common/flash/FSound.cpp @@ -0,0 +1,232 @@ +#include "Macromedia.h" +#include "FSound.h" + +// +// The Low Level sound object +// + +const S32 FSound::kRateTable[4] = {sndRate5K, sndRate11K, sndRate22K, sndRate44K}; +const int FSound::kRateShiftTable[4] = {3, 2, 1, 0}; + +void FSound::Init() +{ + format = 0; + nSamples = 0; + samples = 0; + dataLen = 0; + delay = 0; +} + +void FSound::Set(WaveFormat *wfmt) +{ + wfmt->wFormatTag = 1; //WAVE_FORMAT_PCM; + wfmt->nSamplesPerSec = Rate(); + wfmt->nChannels = NChannels(); + wfmt->wBitsPerSample = BitsPerSample(); + wfmt->nBlockAlign = (wfmt->wBitsPerSample * wfmt->nChannels) / 8; + wfmt->nAvgBytesPerSec = wfmt->nBlockAlign * wfmt->nSamplesPerSec; + + //wfmt->cbSize = 0; +} + +// +// ADPCM tables +// + +static const int indexTable2[2] = { + -1, 2, +}; + +// Is this ok? +static const int indexTable3[4] = { + -1, -1, 2, 4, +}; + +static const int indexTable4[8] = { + -1, -1, -1, -1, 2, 4, 6, 8, +}; + +static const int indexTable5[16] = { + -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16, +}; + +static const int *indexTables[] = { + indexTable2, + indexTable3, + indexTable4, + indexTable5}; + +static const int stepsizeTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767}; + +// +// The Compressor +// + +FSoundComp::FSoundComp(FSound *snd, S32 nb) +{ + // assert(snd->CompressFormat() == sndCompressADPCM); + isStereo = snd->Stereo(); + is8Bit = snd->Is8Bit(); + + nBits = nb; + nSamples = 0; + + len = 0; + + bitBuf = 0; + bitPos = 0; +} + +void FSoundComp::WriteBits(std::vector *stream) +{ + if (stream) { + // Actually write the bits... + while (bitPos >= 8) { + // May need to rewrite this line + // recorder->PutByte((U8)(bitBuf >> (bitPos-8))); + stream->push_back((U8)(bitBuf >> (bitPos - 8))); + + bitPos -= 8; + len++; + } + } else { + // Just counting... + len += bitPos / 8; + bitPos &= 0x7; + } +} + +void FSoundComp::Flush(std::vector *stream) +{ + WriteBits(stream); + if (bitPos > 0) { + PutBits(0, 8 - bitPos, stream); + WriteBits(stream); + } +} + +void FSoundComp::Compress16(S16 *src, S32 n, std::vector *stream) +{ + if (nSamples == 0) { + // Emit the compression settings + PutBits(nBits - 2, 2, stream); + } + + int sn = isStereo ? 2 : 1; + const int *indexTable = indexTables[nBits - 2]; + while (n-- > 0) { + nSamples++; + if ((nSamples & 0xfff) == 1) { + // We emit a header every 4096 samples so we can seek quickly + for (int i = 0; i < sn; i++) { + // Pick an initial index value + S32 d = abs(src[sn] - src[0]); + int k = 0; + while (k < 63 && stepsizeTable[k] < d) + k++; + + PutBits(valpred[i] = *src++, 16, stream); + PutBits(index[i] = k, 6, stream); + } + + } else { + // Generate a delta value + for (int i = 0; i < sn; i++) { + /* Step 1 - compute difference with previous value */ + S32 diff = *src++ - valpred[i]; /* Difference between val and valprev */ + int sign; + if (diff < 0) { + sign = 1 << (nBits - 1); + diff = -diff; + } else { + sign = 0; + } + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this is + ** that even if you have fast mul/div hardware you cannot put it to + ** good use since the fixup would be too expensive. + */ + int step = stepsizeTable[index[i]]; /* Stepsize */ + S32 delta = 0; /* Current adpcm output value */ + S32 vpdiff = 0; /* Current change to valpred */ + + int k = 1 << (nBits - 2); + do { + if (diff >= step) { + delta |= k; + diff -= step; + vpdiff += step; + } + step >>= 1; + k >>= 1; + } while (k); + vpdiff += step; // add the 0.5 + + /* Step 3 - Update previous value */ + if (sign) + valpred[i] -= vpdiff; + else + valpred[i] += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if (valpred[i] != (S16)valpred[i]) + valpred[i] = valpred[i] < 0 ? -32768 : 32767; + assert(valpred[i] <= 32767 && valpred[i] >= -32768); + + /* Step 5 - Assemble value, update index and step values */ + index[i] += indexTable[delta]; + if (index[i] < 0) + index[i] = 0; + else if (index[i] > 88) + index[i] = 88; + + delta |= sign; + + /* Step 6 - Output value */ + PutBits(delta, nBits, stream); + } + } + } +} + +inline void Filter8to16(U8 *src, S16 *dst, S32 n) +// Can work in place +{ + src += n; + dst += n; + while (n--) + *(--dst) = ((S16) * (--src) - 128) << 8; +} + +void FSoundComp::Compress(void *src, S32 n, std::vector *stream) +{ + if (is8Bit) { + S16 buf[4096]; + U8 *s = (U8 *)src; + while (n > 0) { + // Expand to 16 bit and compress + S32 nb = std::min((S32)4096, n); + Filter8to16(s, buf, nb); + Compress16(buf, nb, stream); + n -= nb; + s += nb; + } + + } else { + Compress16((S16 *)src, n, stream); + } +} diff --git a/toonz/sources/common/flash/FSound.h b/toonz/sources/common/flash/FSound.h new file mode 100644 index 0000000..127ef26 --- /dev/null +++ b/toonz/sources/common/flash/FSound.h @@ -0,0 +1,146 @@ +#ifndef SNDMIX_INCLUDED +#define SNDMIX_INCLUDED + +// #include +// #include +#include +#include +#include + +#include "Macromedia.h" +#include "FFixed.h" + +struct WaveFormat { + U16 wFormatTag; /* format type */ + U16 nChannels; /* number of channels (i.e. mono, stereo...) */ + U32 nSamplesPerSec; /* sample rate */ + U32 nAvgBytesPerSec; /* for buffer estimation */ + U16 nBlockAlign; /* block size of data */ + U16 wBitsPerSample; /* number of bits per sample of mono data */ + U16 cbSize; /* the count in bytes of the size of */ +}; + +// Our supported sample rates +// Note that sndRate5K is really 5512.5khz +// Use defines instead of an enum since these must be 32 bits on all platforms +#define sndRate5K 5512L +#define sndRate11K 11025L +#define sndRate22K 22050L +#define sndRate44K 44100L + +const int sndMono = 0x0; +const int sndStereo = 0x1; + +const int snd8Bit = 0x0; +const int snd16Bit = 0x2; + +const int snd5K = 0 << 2; +const int snd11K = 1 << 2; +const int snd22K = 2 << 2; +const int snd44K = 3 << 2; + +const int sndCompressNone = 0x00; // we could add 14 more compression types here... +const int sndCompressADPCM = 0x10; +const int sndCompressMP3 = 0x20; +const int sndCompressNoneI = 0x30; // save out in intel byte order +const int sndRateMask = 0x3 << 2; +const int sndCompressMask = 0xF0; + +enum { // Sound format types + snd5K8Mono = 0, + snd5K8Stereo, + snd5K16Mono, + snd5K16Stereo, + snd11K8Mono, + snd11K8Stereo, + snd11K16Mono, + snd11K16Stereo, + snd22K8Mono, + snd22K8Stereo, + snd22K16Mono, + snd22K16Stereo, + snd44K8Mono, + snd44K8Stereo, + snd44K16Mono, + snd44K16Stereo +}; + +// This object defines a sound sample +class FSound +{ +public: + int format; + S32 nSamples; // the number of samples - duration = nSamples/Rate() + void *samples; // this should probably be a handle on Mac + S32 dataLen; // length in bytes of samples - set only if needed + S32 delay; // MP3 compression has a delay before the real sound data + + static const S32 kRateTable[4]; + static const int kRateShiftTable[4]; + +public: + void Init(); + + S32 Rate() { return kRateTable[(format >> 2) & 0x3]; } + int RateShift() { return kRateShiftTable[(format >> 2) & 0x3]; } + bool Stereo() { return (format & sndStereo) != 0; } + int NChannels() { return (format & sndStereo) ? 2 : 1; } + bool Is8Bit() { return (format & snd16Bit) == 0; } + int BitsPerSample() { return (format & snd16Bit) ? 16 : 8; } + int BytesPerSample() { return (format & snd16Bit) ? 2 : 1; } + int CompressFormat() { return format & sndCompressMask; } + bool Compressed() { return (format & sndCompressMask) != 0; } + + // Manage the duration in 44kHz units + S32 GetDuration44() { return nSamples << RateShift(); } + void SetDuration44(S32 d) { nSamples = d >> RateShift(); } + + int BytesPerBlock() { return BytesPerSample() * NChannels(); } + S32 SizeBytes() { return nSamples * BytesPerBlock(); } + + void Set(WaveFormat *); +}; + +class FSoundComp +{ +private: + BOOL isStereo; + BOOL is8Bit; + int nBits; // number of bits in each sample + + S32 nSamples; // samples compressed so far + + S32 valpred[2]; // ADPCM state + int index[2]; + + // The Destination + S32 len; // default is to just calculate the size + + // State for writing bits + unsigned int bitBuf; + int bitPos; + +public: + FSoundComp(FSound *snd, S32 numberBits); // numberBits from 2-5 + ~FSoundComp() { assert(bitPos == 0); } + + void Compress(void *src, S32 n, std::vector *stream); + void Flush(std::vector *stream); + +private: + void Compress16(S16 *src, S32 n, std::vector *stream); + + // Write variable width bit fields + void WriteBits(std::vector *stream); // empty the buffer of whole bytes + + void PutBits(S32 v, int n, std::vector *stream) + { + assert(n <= 16); + if (bitPos + n > 32) + WriteBits(stream); + bitBuf = (bitBuf << n) | (v & ~(0xFFFFFFFFL << n)); + bitPos += n; + } +}; + +#endif diff --git a/toonz/sources/common/flash/Macromedia.h b/toonz/sources/common/flash/Macromedia.h new file mode 100644 index 0000000..566ef05 --- /dev/null +++ b/toonz/sources/common/flash/Macromedia.h @@ -0,0 +1,514 @@ +// Copyright © 1999 Middlesoft, Inc. All rights reserved. +// First Created By Lee Thomason. +// First Created On 09/08/1999. +// Last Modified On 11/09/1999. +// Last Modified On 18/06/2002 by DV for Fixes +/**************************************************************************************** + + File Summary: Macromedia.h + + This header file defines various structs and enums used by Flash File Format SDK + low-level manager. + +****************************************************************************************/ + +#ifndef _MACROMEDIA_H_ +#define _MACROMEDIA_H_ + +#ifdef WIN32 // added by DV +#pragma warning(disable : 4786) +#endif + +#include "tcommon.h" +#include +#include +#include + +class FSWFStream; + +#ifdef _DEBUG +#ifndef DEBUG +#define DEBUG +#endif +#endif + +// Some basic defines for debugging: +#ifdef DEBUG +#define FLASHOUTPUT printf +#define FLASHASSERT assert +#define FLASHPRINT printf +// +// #define FLASHOUTPUT ((void)0) +// #define FLASHASSERT ((void)0) +// #define FLASHPRINT ((void)0) +#else +inline void nothing(...) +{ +} +#define FLASHOUTPUT nothing //((void)0) +#define FLASHASSERT nothing //((void)0) +#define FLASHPRINT printf +#endif + +/* // removed by DV +#ifndef min + #define min( a, b ) ( ( a < b ) ? a : b ) +#endif +#ifndef max + #define max( a, b ) ( ( a > b ) ? a : b ) +#endif +*/ + +// Global Types +typedef float FLOAT; +typedef TUINT32 U32, *P_U32, **PP_U32; +typedef TINT32 S32, *P_S32, **PP_S32; +typedef unsigned short U16, *P_U16, **PP_U16; +typedef signed short S16, *P_S16, **PP_S16; +typedef unsigned char U8, *P_U8, **PP_U8; +typedef signed char S8, *P_S8, **PP_S8; +typedef TINT32 SFIXED, *P_SFIXED; +typedef TINT32 SCOORD, *P_SCOORD; +typedef int BOOL; + +const SFIXED Fixed1 = 0x00010000; +const SCOORD SCoord1 = 20; + +typedef struct SRECT { + SCOORD xmin; + SCOORD xmax; + SCOORD ymin; + SCOORD ymax; +} SRECT, *P_SRECT; + +const U8 Snd5k = 0; +const U8 Snd11k = 1; +const U8 Snd22k = 2; +const U8 Snd44k = 3; + +const U8 Snd8Bit = 0; +const U8 Snd16Bit = 1; + +const U8 SndMono = 0; +const U8 SndStereo = 1; + +typedef struct SSound { + U8 format; // 0 none, 1 PCM + U8 rate; // Snd5k...Snd44k + U8 size; // 0 8bit, 1 16bit + U8 type; // 0 mono, 1 stereo + U32 sampleCount; // the number of samples + U32 soundSize; // the number of bytes in the sample + U8 *sound; // pointer to the sound data +} SSound, *P_SSound; + +typedef struct SPOINT { + SCOORD x; + SCOORD y; +} SPOINT, *P_SPOINT; + +// Start Sound Flags +enum { + soundHasInPoint = 0x01, + soundHasOutPoint = 0x02, + soundHasLoops = 0x04, + soundHasEnvelope = 0x08 + + // the upper 4 bits are reserved for synchronization flags +}; + +enum { + fillSolid = 0x00, + fillGradient = 0x10, + fillLinearGradient = 0x10, + fillRadialGradient = 0x12, + fillMaxGradientColors = 8, + // Texture/bitmap fills + fillTiledBits = 0x40, // if this bit is set, must be a bitmap pattern + fillClippedBits = 0x41 +}; + +enum { + CURVED_EDGE = 0, + STRAIGHT_EDGE = 1 +}; + +enum { + NOT_EDGE_REC = 0, + EDGE_REC = 1 +}; + +// Flags for defining a shape character +enum { + // These flag codes are used for state changes - and as return values from ShapeParser::GetEdge() + eflagsMoveTo = 0x01, + eflagsFill0 = 0x02, + eflagsFill1 = 0x04, + eflagsLine = 0x08, + eflagsNewStyles = 0x10, + + eflagsEnd = 0x80 // a state change with no change marks the end +}; + +#ifndef NULL +#define NULL 0 +#endif + +// Tag values that represent actions or data in a Flash script. +enum { + stagEnd = 0, + stagShowFrame = 1, + stagDefineShape = 2, + stagFreeCharacter = 3, + stagPlaceObject = 4, + stagRemoveObject = 5, + stagDefineBits = 6, + stagDefineButton = 7, + stagJPEGTables = 8, + stagSetBackgroundColor = 9, + stagDefineFont = 10, + stagDefineText = 11, + stagDoAction = 12, + stagDefineFontInfo = 13, + stagDefineSound = 14, // Event sound tags. + stagStartSound = 15, + stagDefineButtonSound = 17, + stagSoundStreamHead = 18, + stagSoundStreamBlock = 19, + stagDefineBitsLossless = 20, // A bitmap using lossless zlib compression. + stagDefineBitsJPEG2 = 21, // A bitmap using an internal JPEG compression table. + stagDefineShape2 = 22, + stagDefineButtonCxform = 23, + stagProtect = 24, // This file should not be importable for editing. + + stagPathsArePostScript = 25, // assume shapes are filled as PostScript style paths + + // These are the new tags for Flash 3. + stagPlaceObject2 = 26, // The new style place w/ alpha color transform and name. + stagRemoveObject2 = 28, // A more compact remove object that omits the character tag (just depth). + + // This tag is used for RealMedia only + stagSyncFrame = 29, // Handle a synchronization of the display list + + stagFreeAll = 31, // Free all of the characters + + stagDefineShape3 = 32, // A shape V3 includes alpha values. + stagDefineText2 = 33, // A text V2 includes alpha values. + stagDefineButton2 = 34, // A button V2 includes color transform, alpha and multiple actions + stagDefineBitsJPEG3 = 35, // A JPEG bitmap with alpha info. + stagDefineBitsLossless2 = 36, // A lossless bitmap with alpha info. + stagDefineSprite = 39, // Define a sequence of tags that describe the behavior of a sprite. + stagNameCharacter = 40, // Name a character definition, character id and a string, (used for buttons, bitmaps, sprites and sounds). + + stagSerialNumber = 41, // a tag command for the Flash Generator customer serial id and cpu information + stagDefineTextFormat = 42, // define the contents of a text block with formating information + + stagFrameLabel = 43, // A string label for the current frame. + stagSoundStreamHead2 = 45, // For lossless streaming sound, should not have needed this... + stagDefineMorphShape = 46, // A morph shape definition + + stagFrameTag = 47, // a tag command for the Flash Generator (WORD duration, STRING label) + stagDefineFont2 = 48, // a tag command for the Flash Generator Font information + stagGenCommand = 49, // a tag command for the Flash Generator intrinsic + stagDefineCommandObj = 50, // a tag command for the Flash Generator intrinsic Command + stagCharacterSet = 51, // defines the character set used to store strings + stagFontRef = 52, // defines a reference to an external font source + + // Flash 4 tags + stagDefineEditText = 37, // an edit text object (bounds, width, font, variable name) + stagDefineVideo = 38, // a reference to an external video stream + + // NOTE: If tag values exceed 255 we need to expand SCharacter::tagCode from a BYTE to a WORD + stagDefineBitsPtr = 1023 // a special tag used only in the editor +}; + +// PlaceObject2 Flags +enum { + splaceMove = 0x01, // this place moves an exisiting object + splaceCharacter = 0x02, // there is a character tag (if no tag, must be a move) + splaceMatrix = 0x04, // there is a matrix (matrix) + splaceColorTransform = 0x08, // there is a color transform (cxform with alpha) + splaceRatio = 0x10, // there is a blend ratio (word) + splaceName = 0x20, // there is an object name (string) + splaceDefineClip = 0x40, // this shape should open or close a clipping bracket (character != 0 to open, character == 0 to close) + splaceCloneExternalSprite = 0x80 // cloning a movie which was loaded externally + // one bit left for expansion +}; + +//ActionConditions +enum { + OverDownToIdle = 1, + IdleToOverDown = 2, + OutDownToIdle = 3, + OutDownToOverDown = 4, + OverDownToOutDown = 5, + OverDownToOverUp = 6, + OverUpToOverDown = 7, + OverUpToIdle = 8, + IdleToOverUp = 9 +}; + +//Clip Action +enum { + //Flash 5- + ClipEventLoad = 0x00000001, + ClipEventEnterFrame = 0x00000002, + ClipEventUnload = 0x00000004, + ClipEventMouseMove = 0x00000008, + ClipEventMouseDown = 0x00000010, + ClipEventMouseUp = 0x00000020, + ClipEventKeyDown = 0x00000040, + ClipEventKeyUp = 0x00000080, + ClipEventData = 0x00000100, + + //Flash 6+ + ClipEventInitialize = 0x00000200, + ClipEventPress = 0x00000400, + ClipEventRelease = 0x00000800, + ClipEventReleaseOutside = 0x00001000, + ClipEventRollOver = 0x00002000, + ClipEventRollOut = 0x00004000, + ClipEventDragOver = 0x00008000, + ClipEventDragOut = 0x00010000, + ClipEventKeyPress = 0x00020000, +}; + +//Key Codes +enum { + ID_KEY_LEFT = 0x01, + ID_KEY_RIGHT = 0x02, + ID_KEY_HOME = 0x03, + ID_KEY_END = 0x04, + ID_KEY_INSERT = 0x05, + ID_KEY_DELETE = 0x06, + ID_KEY_CLEAR = 0x07, + ID_KEY_BACKSPACE = 0x08, + ID_KEY_ENTER = 0x0D, + ID_KEY_UP = 0x0E, + ID_KEY_DOWN = 0x0F, + ID_KEY_PAGE_UP = 0x10, + ID_KEY_PAGE_DOWN = 0x11, + ID_KEY_TAB = 0x12 +}; + +// Action codes +enum { + // Flash 1 and 2 actions + sactionHasLength = 0x80, + sactionNone = 0x00, + sactionGotoFrame = 0x81, // frame num (WORD) + sactionGetURL = 0x83, // url (STR), window (STR) + sactionNextFrame = 0x04, + sactionPrevFrame = 0x05, + sactionPlay = 0x06, + sactionStop = 0x07, + sactionToggleQuality = 0x08, + sactionStopSounds = 0x09, + sactionWaitForFrame = 0x8A, // frame needed (WORD), actions to skip (BYTE) + + // Flash 3 Actions + sactionSetTarget = 0x8B, // name (STR) + sactionGotoLabel = 0x8C, // name (STR) + + // Flash 4 Actions + sactionAdd = 0x0A, // Stack IN: number, number, OUT: number + sactionSubtract = 0x0B, // Stack IN: number, number, OUT: number + sactionMultiply = 0x0C, // Stack IN: number, number, OUT: number + sactionDivide = 0x0D, // Stack IN: dividend, divisor, OUT: number + sactionEquals = 0x0E, // Stack IN: number, number, OUT: bool + sactionLess = 0x0F, // Stack IN: number, number, OUT: bool + sactionAnd = 0x10, // Stack IN: bool, bool, OUT: bool + sactionOr = 0x11, // Stack IN: bool, bool, OUT: bool + sactionNot = 0x12, // Stack IN: bool, OUT: bool + sactionStringEquals = 0x13, // Stack IN: string, string, OUT: bool + sactionStringLength = 0x14, // Stack IN: string, OUT: number + sactionStringAdd = 0x21, // Stack IN: string, strng, OUT: string + sactionStringExtract = 0x15, // Stack IN: string, index, count, OUT: substring + sactionPush = 0x96, // type (BYTE), value (STRING or FLOAT) + sactionPop = 0x17, // no arguments + sactionToInteger = 0x18, // Stack IN: number, OUT: integer + sactionJump = 0x99, // offset (WORD) + sactionIf = 0x9D, // offset (WORD) Stack IN: bool + sactionCall = 0x9E, // Stack IN: name + sactionGetVariable = 0x1C, // Stack IN: name, OUT: value + sactionSetVariable = 0x1D, // Stack IN: name, value + sactionGetURL2 = 0x9A, // method (BYTE) Stack IN: url, window + sactionGotoFrame2 = 0x9F, // flags (BYTE) Stack IN: frame + sactionSetTarget2 = 0x20, // Stack IN: target + sactionGetProperty = 0x22, // Stack IN: target, property, OUT: value + sactionSetProperty = 0x23, // Stack IN: target, property, value + sactionCloneSprite = 0x24, // Stack IN: source, target, depth + sactionRemoveSprite = 0x25, // Stack IN: target + sactionTrace = 0x26, // Stack IN: message + sactionStartDrag = 0x27, // Stack IN: no constraint: 0, center, target + // constraint: x1, y1, x2, y2, 1, center, target + sactionEndDrag = 0x28, // no arguments + sactionStringLess = 0x29, // Stack IN: string, string, OUT: bool + sactionWaitForFrame2 = 0x8D, // skipCount (BYTE) Stack IN: frame + sactionRandomNumber = 0x30, // Stack IN: maximum, OUT: result + sactionMBStringLength = 0x31, // Stack IN: string, OUT: length + sactionCharToAscii = 0x32, // Stack IN: character, OUT: ASCII code + sactionAsciiToChar = 0x33, // Stack IN: ASCII code, OUT: character + sactionGetTime = 0x34, // Stack OUT: milliseconds since Player start + sactionMBStringExtract = 0x35, // Stack IN: string, index, count, OUT: substring + sactionMBCharToAscii = 0x36, // Stack IN: character, OUT: ASCII code + sactionMBAsciiToChar = 0x37, // Stack IN: ASCII code, OUT: character + + // Flash 5 Actions + sactionConstantPool = 0x88, // create a set of constant + sactionLess2 = 0x48, // Stack IN: number, number, OUT: bool + sactionEquals2 = 0x49, // Stack IN: number, number, OUT: bool + sactionCallMethod = 0x52, // Stack IN: string, string, number, [number] OUT: return value or undefined + + // Reserved for Quicktime + sactionQuickTime = 0xAA // I think this is what they are using... +}; + +enum { + kStringType = 0, + kFloatType = 1 +}; + +enum { + kSpritePosX = 0, + kSpritePosY, + kSpriteScaleX, + kSpriteScaleY, + kSpriteCurFrame, // (can only get but not set) + kSpriteTotalframes, // (can only get but not set) + kSpriteAlpha, // (a value between 0 and 100 %) + kSpriteVisible, // (if zero this means we don't hit test the object) + kSpriteWidth, // (can only get, but not set) + kSpriteHeight, // (can only get, but not set), + kSpriteRotate, + kSpriteTarget, + kSpriteLastFrameLoaded, + kSpriteName, + kSpriteDropTarget, + kSpriteURL, + kSpriteHighQuality, // (global) + kSpriteFocusRect, // (global) + kSpriteSoundBufferTime // (global) +}; + +// Mouse target conditions +enum { + stargetMouseEnter = 1, + stargetMouseExit = 2, + stargetMouseDown = 3, + stargetMouseUp = 4 +}; + +// Bitmap Alpha types +enum { + sbitsAlphaFlag = 0, // just a flag that the alpha channel is valid + sbitsAlphaCTab = 1, // alpha values for a color table + sbitsAlphaMask = 2 // a complete alpha mask for a jpeg image +}; + +// Server Packet Flags +enum { + spktObject = 0x00, // packet types + spktFrame = 0x01, + spktMask = 0x03, + + spktResend = 0x04, // flags for object packets + + spktSeekPoint = 0x04, // flags for frame packets + spktKeyFrame = 0x08 + + // Upper 4 bits are reserved for a sequence number +}; + +// Template Text Flags. +enum { + stextEnd = 0x00, // end of text flag + stextStyle = 0x80, // font style: followed by 8-bit flags (bold, italic, etc...) + stextFont = 0x81, // font identifier: followed by 16-bit font identifier + stextSize = 0x82, // font size: followed by 16-bit value in twips + stextColor = 0x83, // font color: followed by 32-bit RGB value + stextPosition = 0x84, // font position: followed by 8-bit position (normal, super or subscript) + stextKerning = 0x85, // font kerning: followed by 16-bit kerning value in twips + stextReserved1 = 0x86, // reserved value + stextReserved2 = 0x87, // reserved value + stextAlignment = 0x88, // paragraph alignment: followed by 8-bit alignment value + stextIndent = 0x89, // paragraph alignment: followed by 16-bit indent value in twips + stextLMargin = 0x8a, // paragraph left margin: followed by 16-bit left margin value in twips + stextRMargin = 0x8b, // paragraph right margin: followed by 16-bit right margin value in twips + stextLeading = 0x8c, // paragraph leading: followed by 16-bit leading value in twips + stextReserved3 = 0x8d, // reserved value + stextReserved4 = 0x8e, // reserved value + stextReserved5 = 0x8f // reserved value +}; + +// Template Text Style Flags +enum { + stextStyleNone = 0x00, + stextStyleBold = 0x01, + stextStyleItalic = 0x02 + // 6 bits left for expansion +}; + +// Template Text Position Values +enum { + stextPosNormal = 0x00, + stextPosSuperScript = 0x01, + stextPosSubScript = 0x02 +}; + +// Template Text Alignment Values +enum { + stextAlignLeft = 0x00, + stextAlignRight = 0x01, + stextAlignCenter = 0x02, + stextAlignJustify = 0x03 +}; + +// Template Text Flags +enum { + sfontFlagsBold = 0x01, + sfontFlagsItalic = 0x02, + sfontFlagsWideCodes = 0x04, + sfontFlagsWideOffsets = 0x08, + sfontFlagsANSI = 0x10, + sfontFlagsUnicode = 0x20, + sfontFlagsShiftJIS = 0x40, + sfontFlagsHasLayout = 0x80 +}; + +// GetURL2 methods +enum { + kHttpDontSend = 0, + kHttpSendUseGet = 1, + kHttpSendUsePost = 2, + kHttpLoadTarget = 0x40, + kHttpLoadVariables = 0x80 +}; + +// Edit Text Flags +enum { + seditTextFlagsHasFont = 0x0001, + seditTextFlagsHasMaxLength = 0x0002, + seditTextFlagsHasTextColor = 0x0004, + seditTextFlagsReadOnly = 0x0008, + seditTextFlagsPassword = 0x0010, + seditTextFlagsMultiline = 0x0020, + seditTextFlagsWordWrap = 0x0040, + seditTextFlagsHasText = 0x0080, + seditTextFlagsUseOutlines = 0x0100, + seditTextFlagsBorder = 0x0800, + seditTextFlagsNoSelect = 0x1000, + seditTextFlagsHasLayout = 0x2000 +}; + +// Drag constrants +enum { + sdragFromPoint = 0, + sdragFromCenter = 1 +}; +enum { + sdragNoConstraint = 0, + sdragRectConstraint = 1 +}; + +#endif diff --git a/toonz/sources/common/psdlib/psd.cpp b/toonz/sources/common/psdlib/psd.cpp new file mode 100644 index 0000000..765ce54 --- /dev/null +++ b/toonz/sources/common/psdlib/psd.cpp @@ -0,0 +1,1292 @@ + + +#if _MSC_VER >= 1400 +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#include "psd.h" +#include "trasterimage.h" +#include "trop.h" +#include "tpixelutils.h" + +/* + The entire content of this file is ridden with LEAKS. A bug has been filed, will hopefully + be dealt with ASAP. + + + L'intero contenuto del file e' pieno di LEAK. Ho inserito la cosa in Bugilla e non ho potuto + fare altro sotto rilascio. Non solo, il codice dei distruttori e' SBAGLIATO - l'ho esplicitamente + disabilitato, tanto non era chiamato cmq. + + E' da rifare usando SMART POINTERS (boost::scoped_ptr<> o tcg::unique_ptr<>, o + std::unique_ptr<> se facciamo l'upgrade del compilatore). + + Da osservare che anche il campo FILE in TPSDReader leaka se viene sganciata un'eccezione... +*/ + +#define LEVEL_NAME_INDEX_SEP "@" + +//----forward declarations +std::string buildErrorString(int error); + +void readChannel(FILE *f, TPSDLayerInfo *li, + TPSDChannelInfo *chan, int channels, + TPSDHeaderInfo *h); +void readLongData(FILE *f, struct dictentry *parent, TPSDLayerInfo *li); +void readByteData(FILE *f, struct dictentry *parent, TPSDLayerInfo *li); +void readKey(FILE *f, struct dictentry *parent, TPSDLayerInfo *li); +void readLayer16(FILE *f, struct dictentry *parent, TPSDLayerInfo *li); +//----end forward declarations + +char swapByte(unsigned char src) +{ + unsigned char out = 0; + for (int i = 0; i < 8; ++i) { + out = out << 1; + out |= (src & 1); + src = src >> 1; + } + return out; +} + +TPSDReader::TPSDReader(const TFilePath &path) : m_shrinkX(1), + m_shrinkY(1), + m_region(TRect()) +{ + m_layerId = 0; + QString name = path.getName().c_str(); + name.append(path.getDottedType().c_str()); + int sepPos = name.indexOf("#"); + int dotPos = name.indexOf(".", sepPos); + name.remove(sepPos, dotPos - sepPos); + m_path = path.getParentDir() + TFilePath(name.toStdString()); + //m_path = path; + QMutexLocker sl(&m_mutex); + openFile(); + if (!doInfo()) { + fclose(m_file); + throw TImageException(m_path, "Do PSD INFO ERROR"); + } + fclose(m_file); +} +TPSDReader::~TPSDReader() +{ + /*for(int i=0; i 64 || m_headerInfo.rows <= 0 || + m_headerInfo.cols <= 0 || m_headerInfo.depth < 0 || m_headerInfo.depth > 32 || m_headerInfo.mode < 0) { + throw TImageException(m_path, "Reading PSD Header Info error"); + return false; + } + } else { + throw TImageException(m_path, "PSD Version not supported"); + return false; + } + } else { + throw TImageException(m_path, "Cannot read Header"); + return false; + } + return true; +} +// Read Color Mode Data Block +bool TPSDReader::doColorModeData() +{ + m_headerInfo.colormodepos = ftell(m_file); + skipBlock(m_file); //skip "color mode data" + return true; +} +// Read Image Resources Block +bool TPSDReader::doImageResources() +{ + //skipBlock(m_file); //skip "image resources" + long len = read4Bytes(m_file); // lunghezza del blocco Image resources + while (len > 0) { + char type[4], name[0x100]; + int id, namelen; + long size; + fread(type, 1, 4, m_file); + id = read2Bytes(m_file); + namelen = fgetc(m_file); + fread(name, 1, NEXT2(1 + namelen) - 1, m_file); + name[namelen] = 0; + size = read4Bytes(m_file); + if (id == 1005) // ResolutionInfo + { + psdByte savepos = ftell(m_file); + long hres, vres; + double hresd, vresd; + hresd = FIXDPI(hres = read4Bytes(m_file)); + read2Bytes(m_file); + read2Bytes(m_file); + vresd = FIXDPI(vres = read4Bytes(m_file)); + m_headerInfo.vres = vresd; + m_headerInfo.hres = hresd; + fseek(m_file, savepos, SEEK_SET); + } + len -= 4 + 2 + NEXT2(1 + namelen) + 4 + NEXT2(size); + fseek(m_file, NEXT2(size), SEEK_CUR); // skip resource block data + } + if (len != 0) + return false; + return true; +} +// Read Layer Info Block (Layers Number and merged alpha) +bool TPSDReader::doLayerAndMaskInfo() +{ + psdByte layerlen; + + m_headerInfo.layersCount = 0; + m_headerInfo.lmilen = read4Bytes(m_file); + m_headerInfo.lmistart = ftell(m_file); + if (m_headerInfo.lmilen) { + // process layer info section + layerlen = read4Bytes(m_file); + m_headerInfo.linfoBlockEmpty = false; + m_headerInfo.mergedalpha = 0; + if (layerlen) { + doLayersInfo(); + } else { + //WARNING: layer info section empty + } + } else { + //WARNING: layer & mask info section empty + } + return true; +} + +// Read Layers Information Block +// It is called by doLayerAndMaskInfo() +bool TPSDReader::doLayersInfo() +{ + m_headerInfo.layersCount = read2Bytes(m_file); + m_headerInfo.linfoBlockEmpty = false; + m_headerInfo.mergedalpha = m_headerInfo.layersCount < 0; + if (m_headerInfo.mergedalpha > 0) { + m_headerInfo.layersCount = -m_headerInfo.layersCount; + } + if (!m_headerInfo.linfoBlockEmpty) { + m_headerInfo.linfo = (TPSDLayerInfo *)mymalloc(m_headerInfo.layersCount * sizeof(struct TPSDLayerInfo)); + int i = 0; + for (i = 0; i < m_headerInfo.layersCount; i++) { + readLayerInfo(i); + } + } + return true; +} +bool TPSDReader::readLayerInfo(int i) +{ + psdByte chlen, extralen, extrastart; + int j, chid, namelen; + TPSDLayerInfo *li = m_headerInfo.linfo + i; + + // process layer record + li->top = read4Bytes(m_file); + li->left = read4Bytes(m_file); + li->bottom = read4Bytes(m_file); + li->right = read4Bytes(m_file); + li->channels = read2UBytes(m_file); + + if (li->bottom < li->top || li->right < li->left || li->channels > 64) // sanity ck + { + // qualcosa è andato storto, skippo il livello + fseek(m_file, 6 * li->channels + 12, SEEK_CUR); + skipBlock(m_file); // skip "layer info: extra data"; + } else { + li->chan = (TPSDChannelInfo *)mymalloc(li->channels * sizeof(struct TPSDChannelInfo)); + li->chindex = (int *)mymalloc((li->channels + 2) * sizeof(int)); + li->chindex += 2; // + + for (j = -2; j < li->channels; ++j) + li->chindex[j] = -1; + + // fetch info on each of the layer's channels + for (j = 0; j < li->channels; ++j) { + chid = li->chan[j].id = read2Bytes(m_file); + chlen = li->chan[j].length = read4Bytes(m_file); + + if (chid >= -2 && chid < li->channels) + li->chindex[chid] = j; + else { + //WARNING: unexpected channel id + } + } + + fread(li->blend.sig, 1, 4, m_file); + fread(li->blend.key, 1, 4, m_file); + li->blend.opacity = fgetc(m_file); + li->blend.clipping = fgetc(m_file); + li->blend.flags = fgetc(m_file); + fgetc(m_file); // padding + + extralen = read4Bytes(m_file); + extrastart = ftell(m_file); + + // layer mask data + if ((li->mask.size = read4Bytes(m_file))) { + li->mask.top = read4Bytes(m_file); + li->mask.left = read4Bytes(m_file); + li->mask.bottom = read4Bytes(m_file); + li->mask.right = read4Bytes(m_file); + li->mask.default_colour = fgetc(m_file); + li->mask.flags = fgetc(m_file); + fseek(m_file, li->mask.size - 18, SEEK_CUR); // skip remainder + li->mask.rows = li->mask.bottom - li->mask.top; + li->mask.cols = li->mask.right - li->mask.left; + } else { + //no layer mask data + } + + skipBlock(m_file); // skip "layer blending ranges"; + + // layer name + li->nameno = (char *)malloc(16); + sprintf(li->nameno, "layer%d", i + 1); + namelen = fgetc(m_file); + li->name = (char *)mymalloc(NEXT4(namelen + 1)); + fread(li->name, 1, NEXT4(namelen + 1) - 1, m_file); + li->name[namelen] = 0; + if (namelen) { + if (li->name[0] == '.') + li->name[0] = '_'; + } + + // process layer's 'additional info' + + li->additionalpos = ftell(m_file); + li->additionallen = extrastart + extralen - li->additionalpos; + doExtraData(li, li->additionallen); + + // leave file positioned at end of layer's data + fseek(m_file, extrastart + extralen, SEEK_SET); + } + return true; +} + +void TPSDReader::doImage(TRasterP &rasP, int layerId) +{ + m_layerId = layerId; + int layerIndex = getLayerInfoIndexById(layerId); + TPSDLayerInfo *li = getLayerInfo(layerIndex); + psdByte imageDataEnd; + // retrieve start data pos + psdByte startPos = ftell(m_file); + if (m_headerInfo.layersCount > 0) { + struct TPSDLayerInfo *lilast = &m_headerInfo.linfo[m_headerInfo.layersCount - 1]; + startPos = lilast->additionalpos + lilast->additionallen; + } + if (layerIndex > 0) { + for (int j = 0; j < layerIndex; j++) { + struct TPSDLayerInfo *liprev = &m_headerInfo.linfo[j]; + for (int ch = 0; ch < liprev->channels; ch++) { + startPos += liprev->chan[ch].length; + } + } + } + fseek(m_file, startPos, SEEK_SET); + + long pixw = li ? li->right - li->left : m_headerInfo.cols; + long pixh = li ? li->bottom - li->top : m_headerInfo.rows; + int channels = li ? li->channels : m_headerInfo.channels; + + if (li == NULL) + fseek(m_file, m_headerInfo.lmistart + m_headerInfo.lmilen, SEEK_SET); + + psdPixel rows = pixh; + psdPixel cols = pixw; + + int ch = 0; + psdByte **rowpos; + + rowpos = (psdByte **)mymalloc(channels * sizeof(psdByte *)); + + for (ch = 0; ch < channels; ++ch) { + psdPixel chrows = li && !m_headerInfo.linfoBlockEmpty && li->chan[ch].id == -2 ? li->mask.rows : rows; + rowpos[ch] = (psdByte *)mymalloc((chrows + 1) * sizeof(psdByte)); + } + + int tnzchannels = 0; + + int depth = m_headerInfo.depth; + switch (m_headerInfo.mode) { + //default: // multichannel, cmyk, lab etc + // split = 1; + case ModeBitmap: + case ModeGrayScale: + case ModeGray16: + case ModeDuotone: + case ModeDuotone16: + tnzchannels = 1; + // check if there is an alpha channel, or if merged data has alpha + if (li ? li->chindex[-1] != -1 : channels > 1 && m_headerInfo.mergedalpha) { + tnzchannels = 2; + } + break; + case ModeIndexedColor: + tnzchannels = 1; + break; + case ModeRGBColor: + case ModeRGB48: + tnzchannels = 3; + if (li ? li->chindex[-1] != -1 : channels > 3 && m_headerInfo.mergedalpha) { + tnzchannels = 4; + } + break; + default: + tnzchannels = channels; + //assert(0); + break; + } + + if (!li || m_headerInfo.linfoBlockEmpty) { // merged channel + TPSDChannelInfo *mergedChans = (TPSDChannelInfo *)mymalloc(channels * sizeof(struct TPSDChannelInfo)); + + readChannel(m_file, NULL, mergedChans, channels, &m_headerInfo); + imageDataEnd = ftell(m_file); + readImageData(rasP, NULL, mergedChans, tnzchannels, rows, cols); + free(mergedChans); + } else { + for (ch = 0; ch < channels; ++ch) { + readChannel(m_file, li, li->chan + ch, 1, &m_headerInfo); + } + imageDataEnd = ftell(m_file); + readImageData(rasP, li, li->chan, tnzchannels, rows, cols); + } + fseek(m_file, imageDataEnd, SEEK_SET); + + for (ch = 0; ch < channels; ++ch) + free(rowpos[ch]); + free(rowpos); +} + +void TPSDReader::load(TRasterImageP &img, int layerId) +{ + QMutexLocker sl(&m_mutex); + TPSDLayerInfo *li = NULL; + int layerIndex = 0; + if (layerId > 0) { + layerIndex = getLayerInfoIndexById(layerId); + li = getLayerInfo(layerIndex); + } + if (layerId < 0) + throw TImageException(m_path, "Layer ID not exists"); + + if (m_headerInfo.mode == 4 || m_headerInfo.depth == 32) { + img = TRasterImageP(); + return; + } + + try { + TRasterP rasP; + openFile(); + doImage(rasP, layerId); + fclose(m_file); + /* + // do savebox + long sbx0 = li ? li->left : 0; + long sby0 = li ? m_headerInfo.rows-li->bottom : 0; + long sbx1 = li ? li->right - 1 : m_headerInfo.cols - 1; + long sby1 = li ? m_headerInfo.rows - li->top - 1 : m_headerInfo.rows - 1; + TRect layerSaveBox; + layerSaveBox = TRect(sbx0,sby0,sbx1,sby1); + TRect imageRect = TRect(0,0,m_headerInfo.cols-1,m_headerInfo.rows-1); + // E' possibile che il layer sia in parte o tutto al di fuori della'immagine + // in questo caso considero solo la parte visibile, cioè che rientra nell'immagine. + // Se è tutta fuori restutuisco TRasterImageP() + layerSaveBox *= imageRect; + + if(layerSaveBox== TRect()) { + img = TRasterImageP(); + return; + } */ + + if (!rasP) { + img = TRasterImageP(); + return; + } // Happens if layer image has 0 rows and (or?) + // cols (dont ask me why, but I've seen it) + TRect layerSaveBox = m_layersSavebox[layerId]; + TRect savebox(layerSaveBox); + TDimension imgSize(rasP->getLx(), rasP->getLy()); + assert(TRect(imgSize).contains(savebox)); + + if (TRasterGR8P ras = rasP) { + TPixelGR8 bgColor; + ras->fillOutside(savebox, bgColor); + img = TRasterImageP(ras); + } else if (TRaster32P ras = rasP) { + TPixel32 bgColor(0, 0, 0, 0); + if (savebox != TRect()) + ras->fillOutside(savebox, bgColor); + else + ras->fill(bgColor); + img = TRasterImageP(ras); + } else if ((TRaster64P)rasP) { + TRaster32P raux(rasP->getLx(), rasP->getLy()); + TRop::convert(raux, rasP); + TPixel32 bgColor(0, 0, 0, 0); + raux->fillOutside(savebox, bgColor); + img = TRasterImageP(raux); + } else { + throw TImageException(m_path, "Invalid Raster"); + } + img->setDpi(m_headerInfo.hres, m_headerInfo.vres); + img->setSavebox(savebox); + } catch (...) { + } +} + +int TPSDReader::getLayerInfoIndexById(int layerId) +{ + int layerIndex = -1; + for (int i = 0; i < m_headerInfo.layersCount; i++) { + TPSDLayerInfo *litemp = m_headerInfo.linfo + i; + if (litemp->layerId == layerId) { + layerIndex = i; + break; + } + } + if (layerIndex < 0 && layerId != 0) + throw TImageException(m_path, "Layer ID not exists"); + return layerIndex; +} +TPSDLayerInfo *TPSDReader::getLayerInfo(int index) +{ + if (index < 0 || index >= m_headerInfo.layersCount) + return NULL; + return m_headerInfo.linfo + index; +} +TPSDHeaderInfo TPSDReader::getPSDHeaderInfo() +{ + return m_headerInfo; +} + +void TPSDReader::readImageData(TRasterP &rasP, TPSDLayerInfo *li, TPSDChannelInfo *chan, + int chancount, psdPixel rows, psdPixel cols) +{ + int channels = li ? li->channels : m_headerInfo.channels; + + short depth = m_headerInfo.depth; + + psdByte savepos = ftell(m_file); + if (rows == 0 || cols == 0) + return; + psdPixel j; + + unsigned char *inrows[4], *rledata; + + int ch, map[4]; + + rledata = (unsigned char *)mymalloc(chan->rowbytes * 2); + + for (ch = 0; ch < chancount; ++ch) { + inrows[ch] = (unsigned char *)mymalloc(chan->rowbytes); + map[ch] = li && chancount > 1 ? li->chindex[ch] : ch; + } + + // find the alpha channel, if needed + if (li && (chancount == 2 || chancount == 4)) { // grey+alpha + if (li->chindex[-1] == -1) { + //WARNING no alpha found?; + } else + map[chancount - 1] = li->chindex[-1]; + } + + // region dimensions with shrink + // x0 e x1 non tengono conto dello shrink. + int x0 = 0; + int x1 = m_headerInfo.cols - 1; + int y0 = 0; + int y1 = m_headerInfo.rows - 1; + + if (!m_region.isEmpty()) { + x0 = m_region.getP00().x; + // se x0 è fuori dalle dimensioni dell'immagine ritorna un'immagine vuota + if (x0 >= m_headerInfo.cols) + return; + x1 = x0 + m_region.getLx() - 1; + // controllo che x1 rimanga all'interno dell'immagine + if (x1 >= m_headerInfo.cols) + x1 = m_headerInfo.cols - 1; + y0 = m_region.getP00().y; + // se y0 è fuori dalle dimensioni dell'immagine ritorna un'immagine vuota + if (y0 >= m_headerInfo.rows) + return; + y1 = y0 + m_region.getLy() - 1; + // controllo che y1 rimanga all'interno dell'immagine + if (y1 >= m_headerInfo.rows) + y1 = m_headerInfo.rows - 1; + } + if (m_shrinkX > x1 - x0) + m_shrinkX = x1 - x0; + if (m_shrinkY > y1 - y0) + m_shrinkY = y1 - y0; + assert(m_shrinkX > 0 && m_shrinkY > 0); + if (m_shrinkX > 1) { + x1 -= (x1 - x0) % m_shrinkX; + } + if (m_shrinkY > 1) { + y1 -= (y1 - y0) % m_shrinkY; + } + assert(x0 <= x1 && y0 <= y1); + + TDimension imgSize((x1 - x0) / m_shrinkX + 1, (y1 - y0) / m_shrinkY + 1); + if (depth == 1 && chancount == 1) { + rasP = TRasterGR8P(imgSize); + } else if (depth == 8 && chancount > 1) { + rasP = TRaster32P(imgSize); + } else if (m_headerInfo.depth == 8 && chancount == 1) { + rasP = TRasterGR8P(imgSize); + } else if (m_headerInfo.depth == 16 && chancount == 1 && m_headerInfo.mergedalpha) { + rasP = TRasterGR8P(imgSize); + } else if (m_headerInfo.depth == 16) { + rasP = TRaster64P(imgSize); + } + + // do savebox + // calcolo la savebox in coordinate dell'immagine + long sbx0 = li ? li->left - x0 : 0; + long sby0 = li ? m_headerInfo.rows - li->bottom - y0 : 0; + long sbx1 = li ? li->right - 1 - x0 : x1 - x0; + long sby1 = li ? m_headerInfo.rows - li->top - 1 - y0 : y1 - y0; + + TRect layerSaveBox; + layerSaveBox = TRect(sbx0, sby0, sbx1, sby1); + + TRect imageRect; + if (!m_region.isEmpty()) + imageRect = TRect(0, 0, m_region.getLx() - 1, m_region.getLy() - 1); + else + imageRect = TRect(0, 0, m_headerInfo.cols - 1, m_headerInfo.rows - 1); + // E' possibile che il layer sia in parte o tutto al di fuori della'immagine + // in questo caso considero solo la parte visibile, cioè che rientra nell'immagine. + // Se è tutta fuori restutuisco TRasterImageP() + layerSaveBox *= imageRect; + + if (layerSaveBox == TRect() || layerSaveBox.isEmpty()) { + return; + } + // Estraggo da rasP solo il rettangolo che si interseca con il livello corrente + // stando attento a prendere i pixel giusti. + int firstXPixIndexOfLayer = layerSaveBox.getP00().x - 1 + m_shrinkX - (abs(layerSaveBox.getP00().x - 1) % m_shrinkX); + int lrx0 = firstXPixIndexOfLayer / m_shrinkX; + int firstLineIndexOfLayer = layerSaveBox.getP00().y - 1 + m_shrinkY - (abs(layerSaveBox.getP00().y - 1) % m_shrinkY); + int lry0 = firstLineIndexOfLayer / m_shrinkY; + int lrx1 = (layerSaveBox.getP11().x - abs(layerSaveBox.getP11().x % m_shrinkX)) / m_shrinkX; + int lry1 = (layerSaveBox.getP11().y - abs(layerSaveBox.getP11().y % m_shrinkY)) / m_shrinkY; + TRect layerSaveBox2 = TRect(lrx0, lry0, lrx1, lry1); + if (layerSaveBox2.isEmpty()) + return; + assert(TRect(imgSize).contains(layerSaveBox2)); + if (li) + m_layersSavebox[li->layerId] = layerSaveBox2; + else + m_layersSavebox[0] = layerSaveBox2; + TRasterP smallRas = rasP->extract(layerSaveBox2); + assert(smallRas); + if (!smallRas) + return; + // Trovo l'indice di colonna del primo pixel del livello che deve essere letto + // L'indice è riferito al livello. + int colOffset = firstXPixIndexOfLayer - layerSaveBox.getP00().x; + assert(colOffset >= 0); + // Trovo l'indice della prima riga del livello che deve essere letta + // L'indice è riferito al livello. + // Nota che nel file photoshop le righe sono memorizzate dall'ultima alla prima. + int rowOffset = abs(sby1) % m_shrinkY; + int rowCount = rowOffset; + //if(m_shrinkY==3) rowCount--; + for (j = 0; j < smallRas->getLy(); j++) { + for (ch = 0; ch < chancount; ++ch) { + /* get row data */ + if (map[ch] < 0 || map[ch] > chancount) { + //warn("bad map[%d]=%d, skipping a channel", i, map[i]); + memset(inrows[ch], 0, chan->rowbytes); // zero out the row + } else + readrow(m_file, chan + map[ch], rowCount, inrows[ch], rledata); + } + // se la riga corrente non rientra nell'immagine salto la copia + if (sby1 - rowCount < 0 || sby1 - rowCount > m_headerInfo.rows - 1) { + rowCount += m_shrinkY; + continue; + } + if (depth == 1 && chancount == 1) { + if (!(layerSaveBox.getP00().x - sbx0 >= 0 && layerSaveBox.getP00().x - sbx0 + smallRas->getLx() / 8 - 1 < chan->rowbytes)) + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + smallRas->lock(); + unsigned char *rawdata = (unsigned char *)smallRas->getRawData(0, smallRas->getLy() - j - 1); + TPixelGR8 *pix = (TPixelGR8 *)rawdata; + int colCount = colOffset; + for (int k = 0; k < smallRas->getLx(); k += 8) { + char value = ~inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]; + pix[k].setValue(value); + pix[k + 1].setValue(value); + pix[k + 2].setValue(value); + pix[k + 3].setValue(value); + pix[k + 4].setValue(value); + pix[k + 5].setValue(value); + pix[k + 6].setValue(value); + pix[k + 7].setValue(value); + colCount += m_shrinkX; + } + smallRas->unlock(); + } else if (depth == 8 && chancount > 1) { + if (!(layerSaveBox.getP00().x - sbx0 >= 0 && layerSaveBox.getP00().x - sbx0 + smallRas->getLx() - 1 < chan->rowbytes)) + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + smallRas->lock(); + unsigned char *rawdata = (unsigned char *)smallRas->getRawData(0, smallRas->getLy() - j - 1); + TPixel32 *pix = (TPixel32 *)rawdata; + int colCount = colOffset; + for (int k = 0; k < smallRas->getLx(); k++) { + if (chancount >= 3) { + pix[k].r = inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]; + pix[k].g = inrows[1][layerSaveBox.getP00().x - sbx0 + colCount]; + pix[k].b = inrows[2][layerSaveBox.getP00().x - sbx0 + colCount]; + if (chancount == 4) // RGB + alpha + pix[k].m = inrows[3][layerSaveBox.getP00().x - sbx0 + colCount]; + else + pix[k].m = 255; + } else if (chancount <= 2) // gray + alpha + { + pix[k].r = inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]; + pix[k].g = inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]; + pix[k].b = inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]; + if (chancount == 2) + pix[k].m = inrows[1][layerSaveBox.getP00().x - sbx0 + colCount]; + else + pix[k].m = 255; + } + colCount += m_shrinkX; + } + + smallRas->unlock(); + } else if (m_headerInfo.depth == 8 && chancount == 1) { + if (!(layerSaveBox.getP00().x - sbx0 >= 0 && layerSaveBox.getP00().x - sbx0 + smallRas->getLx() - 1 < chan->rowbytes)) + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + smallRas->lock(); + unsigned char *rawdata = (unsigned char *)smallRas->getRawData(0, smallRas->getLy() - j - 1); + + TPixelGR8 *pix = (TPixelGR8 *)rawdata; + int colCount = colOffset; + for (int k = 0; k < smallRas->getLx(); k++) { + pix[k].setValue(inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]); + colCount += m_shrinkX; + } + smallRas->unlock(); + } else if (m_headerInfo.depth == 16 && chancount == 1 && m_headerInfo.mergedalpha) // mergedChannels + { + if (!(layerSaveBox.getP00().x - sbx0 >= 0 && layerSaveBox.getP00().x - sbx0 + smallRas->getLx() - 1 < chan->rowbytes)) + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + smallRas->lock(); + unsigned char *rawdata = (unsigned char *)smallRas->getRawData(0, smallRas->getLy() - j - 1); + TPixelGR8 *pix = (TPixelGR8 *)rawdata; + int colCount = colOffset; + for (int k = 0; k < smallRas->getLx(); k++) { + pix[k].setValue(inrows[0][layerSaveBox.getP00().x - sbx0 + colCount]); + colCount += m_shrinkX; + } + smallRas->unlock(); + } else if (m_headerInfo.depth == 16) { + if (!(layerSaveBox.getP00().x - sbx0 >= 0 && layerSaveBox.getP00().x - sbx0 + smallRas->getLx() - 1 < chan->rowbytes)) + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + smallRas->lock(); + unsigned short *rawdata = (unsigned short *)smallRas->getRawData(0, smallRas->getLy() - j - 1); + TPixel64 *pix = (TPixel64 *)rawdata; + int colCount = colOffset; + for (int k = 0; k < smallRas->getLx(); k++) { + if (chancount >= 3) { + pix[k].r = swapShort(((psdUint16 *)inrows[0])[layerSaveBox.getP00().x - sbx0 + colCount]); + pix[k].g = swapShort(((psdUint16 *)inrows[1])[layerSaveBox.getP00().x - sbx0 + colCount]); + pix[k].b = swapShort(((psdUint16 *)inrows[2])[layerSaveBox.getP00().x - sbx0 + colCount]); + } else if (chancount <= 2) { + pix[k].r = swapShort(((psdUint16 *)inrows[0])[layerSaveBox.getP00().x - sbx0 + colCount]); + pix[k].g = swapShort(((psdUint16 *)inrows[0])[layerSaveBox.getP00().x - sbx0 + colCount]); + pix[k].b = swapShort(((psdUint16 *)inrows[0])[layerSaveBox.getP00().x - sbx0 + colCount]); + if (chancount == 2) + pix[k].m = swapShort(((psdUint16 *)inrows[1])[layerSaveBox.getP00().x - sbx0 + colCount]); + } + if (chancount == 4) { + pix[k].m = swapShort(((psdUint16 *)inrows[3])[layerSaveBox.getP00().x - sbx0 + colCount]); + } else + pix[k].m = 0xffff; + colCount += m_shrinkX; + } + smallRas->unlock(); + } else { + throw TImageException(m_path, "Unable to read image with this depth and channels values"); + } + rowCount += m_shrinkY; + } + fseek(m_file, savepos, SEEK_SET); // restoring filepos + + free(rledata); + for (ch = 0; ch < chancount; ++ch) + free(inrows[ch]); +} + +void TPSDReader::doExtraData(TPSDLayerInfo *li, psdByte length) +{ + static struct dictentry extradict[] = { + // v4.0 + {0, "levl", "LEVELS", "Levels", NULL /*adj_levels*/}, + {0, "curv", "CURVES", "Curves", NULL /*adj_curves*/}, + {0, "brit", "BRIGHTNESSCONTRAST", "Brightness/contrast", NULL}, + {0, "blnc", "COLORBALANCE", "Color balance", NULL}, + {0, "hue ", "HUESATURATION4", "Old Hue/saturation, Photoshop 4.0", NULL /*adj_huesat4*/}, + {0, "hue2", "HUESATURATION5", "New Hue/saturation, Photoshop 5.0", NULL /*adj_huesat5*/}, + {0, "selc", "SELECTIVECOLOR", "Selective color", NULL /*adj_selcol*/}, + {0, "thrs", "THRESHOLD", "Threshold", NULL}, + {0, "nvrt", "INVERT", "Invert", NULL}, + {0, "post", "POSTERIZE", "Posterize", NULL}, + // v5.0 + {0, "lrFX", "EFFECT", "Effects layer", NULL /*ed_layereffects*/}, + {0, "tySh", "TYPETOOL5", "Type tool (5.0)", NULL /*ed_typetool*/}, + {0, "luni", "-UNICODENAME", "Unicode layer name", NULL /*ed_unicodename*/}, + {0, "lyid", "-LAYERID", "Layer ID", readLongData}, // '-' prefix means keep tag value on one line + // v6.0 + {0, "lfx2", "OBJECTEFFECT", "Object based effects layer", NULL /*ed_objecteffects*/}, + {0, "Patt", "PATTERN", "Pattern", NULL}, + {0, "Pat2", "PATTERNCS", "Pattern (CS)", NULL}, + {0, "Anno", "ANNOTATION", "Annotation", NULL /*ed_annotation*/}, + {0, "clbl", "-BLENDCLIPPING", "Blend clipping", readByteData}, + {0, "infx", "-BLENDINTERIOR", "Blend interior", readByteData}, + {0, "knko", "-KNOCKOUT", "Knockout", readByteData}, + {0, "lspf", "-PROTECTED", "Protected", readLongData}, + {0, "lclr", "SHEETCOLOR", "Sheet color", NULL}, + {0, "fxrp", "-REFERENCEPOINT", "Reference point", NULL /*ed_referencepoint*/}, + {0, "grdm", "GRADIENT", "Gradient", NULL}, + {0, "lsct", "-SECTION", "Section divider", readLongData}, // CS doc + {0, "SoCo", "SOLIDCOLORSHEET", "Solid color sheet", NULL /*ed_versdesc*/}, // CS doc + {0, "PtFl", "PATTERNFILL", "Pattern fill", NULL /*ed_versdesc*/}, // CS doc + {0, "GdFl", "GRADIENTFILL", "Gradient fill", NULL /*ed_versdesc*/}, // CS doc + {0, "vmsk", "VECTORMASK", "Vector mask", NULL}, // CS doc + {0, "TySh", "TYPETOOL6", "Type tool (6.0)", NULL /*ed_typetool*/}, // CS doc + {0, "ffxi", "-FOREIGNEFFECTID", "Foreign effect ID", readLongData}, // CS doc (this is probably a key too, who knows) + {0, "lnsr", "-LAYERNAMESOURCE", "Layer name source", readKey}, // CS doc (who knew this was a signature? docs fail again - and what do the values mean?) + {0, "shpa", "PATTERNDATA", "Pattern data", NULL}, // CS doc + {0, "shmd", "METADATASETTING", "Metadata setting", NULL /*ed_metadata*/}, // CS doc + {0, "brst", "BLENDINGRESTRICTIONS", "Channel blending restrictions", NULL}, // CS doc + // v7.0 + {0, "lyvr", "-LAYERVERSION", "Layer version", readLongData}, // CS doc + {0, "tsly", "-TRANSPARENCYSHAPES", "Transparency shapes layer", readByteData}, // CS doc + {0, "lmgm", "-LAYERMASKASGLOBALMASK", "Layer mask as global mask", readByteData}, // CS doc + {0, "vmgm", "-VECTORMASKASGLOBALMASK", "Vector mask as global mask", readByteData}, // CS doc + // CS + {0, "mixr", "CHANNELMIXER", "Channel mixer", NULL}, // CS doc + {0, "phfl", "PHOTOFILTER", "Photo Filter", NULL}, // CS doc + + {0, "Lr16", "LAYER16", "Layer 16", readLayer16}, + {0, NULL, NULL, NULL, NULL}}; + + while (length >= 12) { + psdByte block = sigkeyblock(m_file, extradict, li); + if (!block) { + //warn("bad signature in layer's extra data, skipping the rest"); + break; + } + length -= block; + } +} + +struct dictentry *TPSDReader::findbykey(FILE *f, struct dictentry *parent, char *key, TPSDLayerInfo *li) +{ + struct dictentry *d; + + for (d = parent; d->key; ++d) + if (!memcmp(key, d->key, 4)) { + //char *tagname = d->tag + (d->tag[0] == '-'); + //fprintf(stderr, "matched tag %s\n", d->tag); + if (d->func) { + psdByte savepos = ftell(f); + //int oneline = d->tag[0] == '-'; + //char *tagname = d->tag + oneline; + if (memcmp(key, "Lr16", 4) == 0) { + doLayersInfo(); + } else + d->func(f, d, li); // parse contents of this datum + fseek(f, savepos, SEEK_SET); + } else { + // there is no function to parse this block. + // because tag is empty in this case, we only need to consider + // parent's one-line-ness. + } + return d; + } + return NULL; +} + +int TPSDReader::sigkeyblock(FILE *f, struct dictentry *dict, TPSDLayerInfo *li) +{ + char sig[4], key[4]; + long len; + struct dictentry *d; + + fread(sig, 1, 4, f); + fread(key, 1, 4, f); + len = read4Bytes(f); + if (!memcmp(sig, "8BIM", 4)) { + if (dict && (d = findbykey(f, dict, key, li)) && !d->func) { + // there is no function to parse this block + //UNQUIET(" (data: %s)\n", d->desc); + } + fseek(f, len, SEEK_CUR); + return len + 12; // return number of bytes consumed + } + return 0; // bad signature +} + +//---------------------------- Utility functions +std::string buildErrorString(int error) +{ + std::string message = ""; + switch (error) { + case 0: + message = "NO Error Found"; + break; + case 1: + message = "Reading File Error"; + break; + case 2: + message = "Opening File Error"; + break; + default: + message = "Unknown Error"; + } + return message; +} + +void readChannel(FILE *f, TPSDLayerInfo *li, + TPSDChannelInfo *chan, //channel info array + int channels, + TPSDHeaderInfo *h) +{ + int comp, ch; + psdByte pos, chpos, rb; + unsigned char *zipdata; + psdPixel count, last, j; + chpos = ftell(f); + + if (li) { + + // If this is a layer mask, the pixel size is a special case + if (chan->id == -2) { + chan->rows = li->mask.rows; + chan->cols = li->mask.cols; + } else { + // channel has dimensions of the layer + chan->rows = li->bottom - li->top; + chan->cols = li->right - li->left; + } + } else { + // merged image, has dimensions of PSD + chan->rows = h->rows; + chan->cols = h->cols; + } + + // Compute image row bytes + rb = ((long)chan->cols * h->depth + 7) / 8; + + // Read compression type + comp = read2UBytes(f); + + // Prepare compressed data for later access: + pos = chpos + 2; + + // skip rle counts, leave pos pointing to first compressed image row + if (comp == RLECOMP) + pos += (channels * chan->rows) << h->version; + + for (ch = 0; ch < channels; ++ch) { + if (!li) + chan[ch].id = ch; + chan[ch].rowbytes = rb; + chan[ch].comptype = comp; + chan[ch].rows = chan->rows; + chan[ch].cols = chan->cols; + chan[ch].filepos = pos; + + if (!chan->rows) + continue; + + // For RLE, we read the row count array and compute file positions. + // For ZIP, read and decompress whole channel. + switch (comp) { + case RAWDATA: + pos += chan->rowbytes * chan->rows; + break; + + case RLECOMP: + /* accumulate RLE counts, to make array of row start positions */ + chan[ch].rowpos = (psdByte *)mymalloc((chan[ch].rows + 1) * sizeof(psdByte)); + last = chan[ch].rowbytes; + for (j = 0; j < chan[ch].rows && !feof(f); ++j) { + count = h->version == 1 ? read2UBytes(f) : (unsigned long)read4Bytes(f); + + if (count > 2 * chan[ch].rowbytes) // this would be impossible + count = last; // make a guess, to help recover + last = count; + + chan[ch].rowpos[j] = pos; + pos += count; + } + if (j < chan[ch].rows) { + // fatal error couldn't read RLE counts + } + chan[ch].rowpos[j] = pos; /* = end of last row */ + break; + + case ZIPWITHOUTPREDICTION: + case ZIPWITHPREDICTION: + if (li) { + pos += chan->length - 2; + + zipdata = (unsigned char *)mymalloc(chan->length); + count = fread(zipdata, 1, chan->length - 2, f); + //if(count < chan->length - 2) + // alwayswarn("ZIP data short: wanted %d bytes, got %d", chan->length, count); + + chan->unzipdata = (unsigned char *)mymalloc(chan->rows * chan->rowbytes); + if (comp == ZIPWITHOUTPREDICTION) + psdUnzipWithoutPrediction(zipdata, count, chan->unzipdata, + chan->rows * chan->rowbytes); + else + psdUnzipWithPrediction(zipdata, count, chan->unzipdata, + chan->rows * chan->rowbytes, + chan->cols, h->depth); + + free(zipdata); + } else { + // WARNING cannot process ZIP compression outside layer + } + break; + default: { + // BAD COMPRESSION TYPE + } + if (li) + fseek(f, chan->length - 2, SEEK_CUR); + break; + } + } + + //if(li && pos != chpos + chan->length) + // alwayswarn("# channel data is %ld bytes, but length = %ld\n", + // pos - chpos, chan->length); + + // set at the end of channel's data + fseek(f, pos, SEEK_SET); +} + +void readLongData(FILE *f, struct dictentry *parent, TPSDLayerInfo *li) +{ + unsigned long id = read4Bytes(f); + if (strcmp(parent->key, "lyid") == 0) + li->layerId = id; + else if (strcmp(parent->key, "lspf") == 0) + li->protect = id; + else if (strcmp(parent->key, "lsct") == 0) + li->section = id; + else if (strcmp(parent->key, "ffxi") == 0) + li->foreignEffectID = id; + else if (strcmp(parent->key, "lyvr") == 0) + li->layerVersion = id; +} + +void readByteData(FILE *f, struct dictentry *parent, TPSDLayerInfo *li) +{ + int id = fgetc(f); + if (strcmp(parent->key, "clbl") == 0) + li->blendClipping = id; + else if (strcmp(parent->key, "infx") == 0) + li->blendInterior = id; + else if (strcmp(parent->key, "knko") == 0) + li->knockout = id; + else if (strcmp(parent->key, "tsly") == 0) + li->transparencyShapes = id; + else if (strcmp(parent->key, "lmgm") == 0) + li->layerMaskAsGlobalMask = id; + else if (strcmp(parent->key, "vmgm") == 0) + li->vectorMaskAsGlobalMask = id; +} + +void readKey(FILE *f, struct dictentry *parent, TPSDLayerInfo *li) +{ + static char key[5]; + if (fread(key, 1, 4, f) == 4) + key[4] = 0; + else + key[0] = 0; // or return NULL? + + if (strcmp(parent->key, "lnsr") == 0) + li->layerNameSource = key; +} + +void readLayer16(FILE *f, struct dictentry *parent, TPSDLayerInfo *li) +{ + //struct psd_header h2 = *psd_header; // a kind of 'nested' set of layers; don't alter main PSD header + + // overwrite main PSD header, mainly because of the 'merged alpha' flag + + // I *think* they mean us to respect the one in Lr16 because in my test data, + // the main layer count is zero, so cannot convey this information. + //dolayerinfo(f, psd_header); + //processlayers(f, psd_header); +} +//---------------------------- END Utility functions + +// TPSD PARSER +TPSDParser::TPSDParser(const TFilePath &path) +{ + m_path = path; + QString name = path.getName().c_str(); + name.append(path.getDottedType().c_str()); + int sepPos = name.indexOf("#"); + int dotPos = name.indexOf(".", sepPos); + name.remove(sepPos, dotPos - sepPos); + TFilePath psdpath = m_path.getParentDir() + TFilePath(name.toStdString()); + m_psdreader = new TPSDReader(psdpath); + doLevels(); +} + +TPSDParser::~TPSDParser() +{ + delete m_psdreader; +} + +void TPSDParser::doLevels() +{ + QString path = m_path.getName().c_str(); + QStringList list = path.split("#"); + m_levels.clear(); + if (list.size() > 1) { + TPSDHeaderInfo psdheader = m_psdreader->getPSDHeaderInfo(); + if (list.contains("frames") && list.at(0) != "frames") { + int firstLayerId = 0; + Level level; + for (int i = 0; i < psdheader.layersCount; i++) { + TPSDLayerInfo *li = m_psdreader->getLayerInfo(i); + long width = li->right - li->left; + long height = li->bottom - li->top; + if (width > 0 && height > 0) { + assert(li->layerId >= 0); + if (i == 0) + firstLayerId = li->layerId; + level.addFrame(li->layerId); + } + } + // non ha importanza quale layerId assegno, l'importante è che esista + level.setLayerId(0); + if (psdheader.layersCount == 0) + level.addFrame(firstLayerId); // succede nel caso in cui la psd non ha blocco layerInfo + m_levels.push_back(level); + } else if (list.size() == 3) { + if (list.at(2) == "group") { + int folderTagOpen = 0; + int scavenge = 0; + for (int i = 0; i < psdheader.layersCount; i++) { + TPSDLayerInfo *li = m_psdreader->getLayerInfo(i); + long width = li->right - li->left; + long height = li->bottom - li->top; + if (width > 0 && height > 0 && folderTagOpen == 0) { + assert(li->layerId >= 0); + Level level(li->name, li->layerId); + level.addFrame(li->layerId); + m_levels.push_back(level); + scavenge = 0; + } else { + if (width != 0 && height != 0) { + m_levels[m_levels.size() - 1 - (scavenge - folderTagOpen)].addFrame(li->layerId); + } else { + if (strcmp(li->name, "") == 0 || strcmp(li->name, "") == 0) { + assert(li->layerId >= 0); + Level level(li->name, li->layerId, true); + m_levels.push_back(level); + folderTagOpen++; + scavenge = folderTagOpen; + } else if (li->section > 0 && li->section <= 3) // vedi specifiche psd + { + assert(li->layerId >= 0); + m_levels[m_levels.size() - 1 - (scavenge - folderTagOpen)].setName(li->name); + m_levels[m_levels.size() - 1 - (scavenge - folderTagOpen)].setLayerId(li->layerId); + folderTagOpen--; + if (folderTagOpen > 0) + m_levels[m_levels.size() - 1 - (scavenge - folderTagOpen)].addFrame(li->layerId, true); + } + } + } + } + if (psdheader.layersCount == 0) // succede nel caso in cui la psd non ha blocco layerInfo + { + Level level; + level.setLayerId(0); + level.addFrame(0); + m_levels.push_back(level); + } + } else + throw TImageException(m_path, "PSD code name error"); + } else if (list.size() == 2) // each psd layer is a tlevel + { + TPSDHeaderInfo psdheader = m_psdreader->getPSDHeaderInfo(); + for (int i = 0; i < psdheader.layersCount; i++) { + TPSDLayerInfo *li = m_psdreader->getLayerInfo(i); + long width = li->right - li->left; + long height = li->bottom - li->top; + if (width > 0 && height > 0) { + assert(li->layerId >= 0); + Level level(li->name, li->layerId); + level.addFrame(li->layerId); + m_levels.push_back(level); + } + } + if (psdheader.layersCount == 0) // succede nel caso in cui la psd non ha blocco layerInfo + { + Level level; + level.setLayerId(0); + level.addFrame(0); + m_levels.push_back(level); + } + } else + throw TImageException(m_path, "PSD code name error"); + } else // list.size()==1. float + { + Level level; + level.setName(m_path.getName()); + level.addFrame(0); + m_levels.push_back(level); + } +} + +void TPSDParser::load(TRasterImageP &rasP, int layerId) +{ + m_psdreader->load(rasP, layerId); +} + +int TPSDParser::getLevelIndexById(int layerId) +{ + int layerIndex = -1; + for (int i = 0; i < (int)m_levels.size(); i++) { + if (m_levels[i].getLayerId() == layerId) { + layerIndex = i; + break; + } + } + if (layerId == 0 && layerIndex < 0) + layerIndex = 0; + if (layerIndex < 0 && layerId != 0) + throw TImageException(m_path, "Layer ID not exists"); + return layerIndex; +} +int TPSDParser::getLevelIdByName(string levelName) +{ + int pos = levelName.find_last_of(LEVEL_NAME_INDEX_SEP); + int counter = 0; + if (pos != string::npos) { + counter = atoi(levelName.substr(pos + 1).c_str()); + levelName = levelName.substr(0, pos); + } + int lyid = -1; + int levelNameCount = 0; + for (int i = 0; i < (int)m_levels.size(); i++) { + if (m_levels[i].getName() == levelName) { + lyid = m_levels[i].getLayerId(); + if (counter == levelNameCount) + break; + levelNameCount++; + } + } + if (lyid == 0 && lyid < 0) + lyid = 0; + if (lyid < 0 && lyid != 0) + throw TImageException(m_path, "Layer ID not exists"); + return lyid; +} +int TPSDParser::getFramesCount(int levelId) +{ + int levelIndex = getLevelIndexById(levelId); + assert(levelIndex >= 0 && levelIndex < (int)m_levels.size()); + return m_levels[levelIndex].getFrameCount(); +} +string TPSDParser::getLevelName(int levelId) +{ + int levelIndex = getLevelIndexById(levelId); + assert(levelIndex >= 0 && levelIndex < (int)m_levels.size()); + return m_levels[levelIndex].getName(); +} +string TPSDParser::getLevelNameWithCounter(int levelId) +{ + string levelName = getLevelName(levelId); + int count = 0; + for (int i = 0; i < (int)m_levels.size(); i++) { + if (m_levels[i].getName() == levelName) { + if (m_levels[i].getLayerId() == levelId) { + break; + } + count++; + } + } + if (count > 0) { + levelName.append(LEVEL_NAME_INDEX_SEP); + string c = QString::number(count).toStdString(); + levelName.append(c); + } + return levelName; +} diff --git a/toonz/sources/common/psdlib/psd.h b/toonz/sources/common/psdlib/psd.h new file mode 100644 index 0000000..e243116 --- /dev/null +++ b/toonz/sources/common/psdlib/psd.h @@ -0,0 +1,305 @@ + + +#ifndef PSD_INCLUDED +#define PSD_INCLUDED + +#include "psdutils.h" +#include "timage_io.h" + +#define REF_LAYER_BY_NAME +using namespace std; + +class TRasterImageP; + +#undef DVAPI +#undef DVVAR + +#ifdef TNZCORE_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +// Photoshop's mode +#define ModeBitmap 0 +#define ModeGrayScale 1 +#define ModeIndexedColor 2 +#define ModeRGBColor 3 +#define ModeCMYKColor 4 +#define ModeHSLColor 5 +#define ModeHSBColor 6 +#define ModeMultichannel 7 +#define ModeDuotone 8 +#define ModeLabColor 9 +#define ModeGray16 10 +#define ModeRGB48 11 +#define ModeLab48 12 +#define ModeCMYK64 13 +#define ModeDeepMultichannel 14 +#define ModeDuotone16 15 + +struct TPSDLayerMaskInfo { + psdByte size; + long top; + long left; + long bottom; + long right; + char default_colour; + char flags; + + psdPixel rows, cols; +}; + +struct TPSDBlendModeInfo { + char sig[4]; + char key[4]; + unsigned char opacity; + unsigned char clipping; + unsigned char flags; +}; + +struct TPSDLayerInfo { + long top; + long left; + long bottom; + long right; + short channels; + + TPSDChannelInfo *chan; + int *chindex; + + unsigned long layerId; + unsigned long protect; + unsigned long section; + unsigned long foreignEffectID; + unsigned long layerVersion; + + int blendClipping; + int blendInterior; + int knockout; + int transparencyShapes; + int layerMaskAsGlobalMask; + int vectorMaskAsGlobalMask; + + TPSDBlendModeInfo blend; + TPSDLayerMaskInfo mask; + + double referencePointX; + double referencePointY; + + char *name; + char *nameno; // "layerNr" + char *unicodeName; + char *layerNameSource; + psdByte additionalpos; + psdByte additionallen; + psdByte filepos; // + + psdByte startDataPos; // Posizione di inizio dati all'interno del file + psdByte dataLength; // lunghezza dati + + // LAYER EFFECTS + unsigned long int *fxCommonStateVersion; + int fxCommonStateVisible; + unsigned long int fxShadowVersion; + float fxShadowBlur; + float fxShadowIntensity; + float fxShadowAngle; + float fxShadowDistance; + + //my tags + bool isFolder; +}; + +struct TPSDHeaderInfo { + + char sig[4]; + short version; + char reserved[6]; + short channels; + long rows; + long cols; + short depth; + short mode; + double vres; // image resource. Vertical resolution + double hres; // image resource. Horizontal resolution + + psdByte colormodepos; + int layersCount; + int mergedalpha; + bool linfoBlockEmpty; + TPSDLayerInfo *linfo; // array of layer info + psdByte lmistart, lmilen; // set by doLayerAndMaskInfo() + psdByte layerDataPos; //set by doInfo +}; + +struct dictentry { + int id; + char *key, *tag, *desc; + void (*func)(FILE *f, struct dictentry *dict, TPSDLayerInfo *li); +}; + +// PSD LIB +// Restituisce eccezioni +class DVAPI TPSDReader +{ + TFilePath m_path; + FILE *m_file; + int m_lx, m_ly; + TPSDHeaderInfo m_headerInfo; + int m_layerId; + int m_shrinkX; + int m_shrinkY; + TRect m_region; + +public: + TPSDReader(const TFilePath &path); + ~TPSDReader(); + TImageReaderP getFrameReader(TFilePath path); + TPSDHeaderInfo getPSDHeaderInfo(); + + void load(TRasterImageP &rasP, int layerId); + TDimension getSize() const { return TDimension(m_lx, m_ly); } + TPSDLayerInfo *getLayerInfo(int index); + int getLayerInfoIndexById(int layerId); + void setShrink(int shrink) + { + m_shrinkX = shrink; + m_shrinkY = shrink; + } + void setShrink(int shrinkX, int shrinkY) + { + m_shrinkX = shrinkX; + m_shrinkY = shrinkY; + } + void setRegion(TRect region) { m_region = region; } + + int getShrinkX() { return m_shrinkX; } + int getShrinkY() { return m_shrinkY; } + TRect getRegion() { return m_region; } + +private: + map m_layersSavebox; + + bool doInfo(); + bool doHeaderInfo(); + bool doColorModeData(); + bool doImageResources(); + bool doLayerAndMaskInfo(); + bool doLayersInfo(); + bool readLayerInfo(int index); + + //void doImage(unsigned char *rasP, TPSDLayerInfo *li); + void doImage(TRasterP &rasP, int layerId); + + void readImageData(TRasterP &rasP, TPSDLayerInfo *li, TPSDChannelInfo *chan, + int chancount, psdPixel rows, psdPixel cols); + int m_error; + TThread::Mutex m_mutex; + int openFile(); + + void doExtraData(TPSDLayerInfo *li, psdByte length); + int sigkeyblock(FILE *f, struct dictentry *dict, TPSDLayerInfo *li); + struct dictentry *findbykey(FILE *f, struct dictentry *parent, char *key, TPSDLayerInfo *li); +}; + +// converts psd layers structure into toonz level structure according to path +class DVAPI TPSDParser +{ + + class Level + { + public: + Level(string nm = "Unknown", int lid = 0, bool is_folder = false) : name(nm), + layerId(lid), + folder(is_folder) + { + } + void addFrame(int layerId, bool isFolder = false) { framesId.push_back(Frame(layerId, isFolder)); } + int getFrameCount() { return (int)framesId.size(); } + string getName() { return name; } + int getLayerId() { return layerId; } + void setName(string nname) { name = nname; } + void setLayerId(int nlayerId) { layerId = nlayerId; } + int getFrameId(int index) + { + assert(index >= 0 && index < (int)framesId.size()); + return framesId[index].layerId; + } + // return true if frame is a subfolder + bool isSubFolder(int frameIndex) + { + assert(frameIndex >= 0 && frameIndex < (int)framesId.size()); + return framesId[frameIndex].isFolder; + } + // return true if the level is built from a psd folder + bool isFolder() { return folder; } + private: + struct Frame { + int layerId; // psd layerId + bool isFolder; + Frame(int layId, bool folder) : layerId(layId), isFolder(folder) + { + } + }; + + string name; // psd name + int layerId; // psd layer id + std::vector framesId; // array of layer ID as frame + bool folder; + }; + + TFilePath m_path; + std::vector m_levels; // layers id + TPSDReader *m_psdreader; //lib +public: + // path define levels construction method + // if path = : + // filename.psd flat image so LevelsCount = 1; + // filename#LAYERID.psd each psd layer as a tlevel + // filename#LAYERID#frames.psd each psd layer as a frame so there is only one tlevel with 1 or more frames; + // filename#LAYERID#folder.psd each psd layer is a tlevel and \ + // each folder is a tlevel such as the psd layers \ + // contained into folder are frames of tlevel + // LAYERID(Integer) is psd layerId + TPSDParser(const TFilePath &path); + ~TPSDParser(); + int getLevelsCount() { return (int)m_levels.size(); } + // load a psd layer + // if layerId == 0 load flat image + void load(TRasterImageP &rasP, int layerId = 0); + + // Returns psd layerID + int getLevelId(int index) + { + assert(index >= 0 && index < (int)m_levels.size()); + return m_levels[index].getLayerId(); + } + bool isFolder(int levelIndex) + { + assert(levelIndex >= 0 && levelIndex < (int)m_levels.size()); + return m_levels[levelIndex].isFolder(); + } + int getLevelIndexById(int levelId); + // Returns layerID by name. Note that the layer name is not unique, so it return the first layer id found. + int getLevelIdByName(string levelName); + int getFrameId(int layerId, int frameIndex) { return m_levels[getLevelIndexById(layerId)].getFrameId(frameIndex); } + int getFramesCount(int levelId); + bool isSubFolder(int levelIndex, int frameIndex) + { + assert(levelIndex >= 0 && levelIndex < (int)m_levels.size()); + return m_levels[levelIndex].isSubFolder(frameIndex); + } + string getLevelName(int levelId); + // Returns level name. + // If there are 2 or more level with the same name then + // returns levelname, levelname__2, etc + string getLevelNameWithCounter(int levelId); + +private: + void doLevels(); // do m_levels +}; + +#endif //TIIO_PSD_H diff --git a/toonz/sources/common/psdlib/psdutils.cpp b/toonz/sources/common/psdlib/psdutils.cpp new file mode 100644 index 0000000..963b07e --- /dev/null +++ b/toonz/sources/common/psdlib/psdutils.cpp @@ -0,0 +1,220 @@ + + +#if _MSC_VER >= 1400 +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#include "zlib.h" + +#include "psdutils.h" + +void readrow(FILE *psd, + TPSDChannelInfo *chan, + psdPixel row, // row index + unsigned char *inbuffer, // dest buffer for the uncompressed row + unsigned char *tmpbuffer) // temp rlebuffer for compressed data, 2xrb in size +{ + psdPixel n = 0, rlebytes; + psdByte pos; + + int seekres = 0; + + switch (chan->comptype) { + case RAWDATA: /* uncompressed */ + pos = chan->filepos + chan->rowbytes * row; + seekres = fseek(psd, pos, SEEK_SET); + if (seekres != -1) + n = fread(inbuffer, 1, chan->rowbytes, psd); + break; + case RLECOMP: + pos = chan->rowpos[row]; + seekres = fseek(psd, pos, SEEK_SET); + if (seekres != -1) { + rlebytes = fread(tmpbuffer, 1, chan->rowpos[row + 1] - pos, psd); + n = unpackrow(inbuffer, tmpbuffer, chan->rowbytes, rlebytes); + } + break; + case ZIPWITHPREDICTION: + case ZIPWITHOUTPREDICTION: + memcpy(inbuffer, chan->unzipdata + chan->rowbytes * row, chan->rowbytes); + return; + } + // if we don't recognise the compression type, skip the row + // FIXME: or would it be better to use the last valid type seen? + + // if(seekres == -1) + // alwayswarn("# can't seek to " LL_L("%lld\n","%ld\n"), pos); + + if (n < chan->rowbytes) { + //warn("row data short (wanted %d, got %d bytes)", chan->rowbytes, n); + // zero out unwritten part of row + memset(inbuffer + n, 0, chan->rowbytes - n); + } +} + +int unpackrow(unsigned char *out, unsigned char *in, + psdPixel outlen, psdPixel inlen) +{ + psdPixel i, len; + int val; + + for (i = 0; inlen > 1 && i < outlen;) { + len = *in++; + --inlen; + + if (len == 128) /* ignore RLE flag value */ + ; + else { + if (len > 128) { + len = 1 + 256 - len; + + val = *in++; + --inlen; + + if ((i + len) <= outlen) + memset(out, val, len); + else { + memset(out, val, outlen - i); // fill to complete row + len = 0; + } + } else { + ++len; + if ((i + len) <= outlen) { + if (len > inlen) + break; + memcpy(out, in, len); + in += len; + inlen -= len; + } else { + memcpy(out, in, outlen - i); // copy to complete row + len = 0; + } + } + out += len; + i += len; + } + } + if (i < outlen) { + //WARNING: not enough RLE data for row; + } + return i; +} + +void skipBlock(FILE *f) +{ + psdByte n = read4Bytes(f); + if (n) { + fseek(f, n, SEEK_CUR); + } else { + } +} + +// Read 2byte unsigned binary value. +// BigEndian. +unsigned read2UBytes(FILE *f) +{ + unsigned n = fgetc(f) << 8; + return n |= fgetc(f); +} +// Read 2byte binary value +// BigEndian. +int read2Bytes(FILE *f) +{ + unsigned n = fgetc(f) << 8; + n |= fgetc(f); + return n < 0x8000 ? n : n - 0x10000; +} + +// Read 4byte signed binary value. +// BigEndian. +long read4Bytes(FILE *f) +{ + long n = fgetc(f) << 24; + n |= fgetc(f) << 16; + n |= fgetc(f) << 8; + return n | fgetc(f); +} + +void *mymalloc(long n) +{ + void *p = malloc(n); + if (p) + return p; + else { + //ALLOCATION ERROR + } + return NULL; +} + +// ZIP COMPRESSION + +// ZIP WITHOUT PREDICTION +// If no error returns 1 else returns 0 +int psdUnzipWithoutPrediction(unsigned char *src_buf, int src_len, + unsigned char *dst_buf, int dst_len) +{ + z_stream stream; + int state; + + memset(&stream, 0, sizeof(z_stream)); + stream.data_type = Z_BINARY; + + stream.next_in = (Bytef *)src_buf; + stream.avail_in = src_len; + stream.next_out = (Bytef *)dst_buf; + stream.avail_out = dst_len; + + if (inflateInit(&stream) != Z_OK) + return 0; + + do { + state = inflate(&stream, Z_PARTIAL_FLUSH); + if (state == Z_STREAM_END) + break; + if (state == Z_DATA_ERROR || state != Z_OK) + break; + } while (stream.avail_out > 0); + + if (state != Z_STREAM_END && state != Z_OK) + return 0; + + return 1; +} + +// ZIP WITH PREDICTION + +int psdUnzipWithPrediction(unsigned char *src_buf, int src_len, + unsigned char *dst_buf, int dst_len, + int row_size, int color_depth) +{ + int status; + int len; + unsigned char *buf; + + status = psdUnzipWithoutPrediction(src_buf, src_len, dst_buf, dst_len); + if (!status) + return status; + + buf = dst_buf; + do { + len = row_size; + if (color_depth == 16) { + while (--len) { + buf[2] += buf[0] + ((buf[1] + buf[3]) >> 8); + buf[3] += buf[1]; + buf += 2; + } + buf += 2; + dst_len -= row_size * 2; + } else { + while (--len) { + *(buf + 1) += *buf; + buf++; + } + buf++; + dst_len -= row_size; + } + } while (dst_len > 0); + + return 1; +} diff --git a/toonz/sources/common/psdlib/psdutils.h b/toonz/sources/common/psdlib/psdutils.h new file mode 100644 index 0000000..c16b041 --- /dev/null +++ b/toonz/sources/common/psdlib/psdutils.h @@ -0,0 +1,52 @@ + + +#include +#include +#include + +#define BLOCKROWS 3 + +#define NEXT2(x) (((x) + 1) & -2) // uguale o successivo (pad) +#define NEXT4(x) (((x) + 3) & -4) // uguale o successivo multiplo di 4 (pad) +#define FIXDPI(x) ((x) / 65536.) // fixedpt + +typedef long psdByte; +typedef unsigned short psdUint16; +typedef long psdPixel; + +enum { RAWDATA, + RLECOMP, + ZIPWITHOUTPREDICTION, + ZIPWITHPREDICTION }; + +struct TPSDChannelInfo { + int id; // channel id + int comptype; // channel's compression type + psdPixel rows, cols, rowbytes; // computed by dochannel() + // FIXME: need depth?? + psdByte length; // channel byte count in file + psdByte filepos; // file offset of channel data (AFTER compression type) + psdByte *rowpos; // row data file positions (RLE ONLY) + unsigned char *unzipdata; // uncompressed data (ZIP ONLY) +}; + +int unpackrow(unsigned char *out, unsigned char *in, psdPixel outlen, psdPixel inlen); + +void readrow(FILE *psd, + TPSDChannelInfo *chan, + psdPixel rowIndex, + unsigned char *inbuffer, + unsigned char *outbuffer); + +void skipBlock(FILE *f); + +void *mymalloc(long n); +unsigned read2UBytes(FILE *f); +int read2Bytes(FILE *f); +long read4Bytes(FILE *f); + +int psdUnzipWithoutPrediction(unsigned char *src_buf, int src_len, + unsigned char *dst_buf, int dst_len); +int psdUnzipWithPrediction(unsigned char *src_buf, int src_len, + unsigned char *dst_buf, int dst_len, + int row_size, int color_depth); diff --git a/toonz/sources/common/tapptools/tcli.cpp b/toonz/sources/common/tapptools/tcli.cpp new file mode 100644 index 0000000..b73519c --- /dev/null +++ b/toonz/sources/common/tapptools/tcli.cpp @@ -0,0 +1,914 @@ + + +#include "tcli.h" +#include "tconvert.h" +#include + +using namespace std; +using namespace TCli; + +//========================================================= +// +// Release +// +//--------------------------------------------------------- +namespace +{ + +//--------------------------------------------------------- + +void printToonzRelease(ostream &out) +{ + out << "Toonz 7.1" << endl; +} + +//--------------------------------------------------------- + +void printLibRelease(ostream &out) +{ + out << "Tnzcore 1.0 - " __DATE__ << endl; +} +//--------------------------------------------------------- + +} //namespace + +//========================================================= +// +// local UsageError +// +//--------------------------------------------------------- + +//========================================================= +// +// local UsageElements +// +//--------------------------------------------------------- + +class SpecialUsageElement : public UsageElement +{ +public: + SpecialUsageElement(string name) : UsageElement(name, " "){}; + void dumpValue(ostream &) const {}; + void resetValue(){}; +}; + +//--------------------------------------------------------- + +static SpecialUsageElement bra("["); +static SpecialUsageElement ket("]"); +static Switcher help("-help", "Print this help page"); +static Switcher release("-release", "Print the current Toonz version"); +static Switcher libRelease("-librelease", ""); +// hidden: print the lib version + +//========================================================= +// +// helper function +// +//--------------------------------------------------------- +namespace +{ + +//--------------------------------------------------------- + +void fetchElement(int index, int &argc, char *argv[]) +{ + if (index >= argc) + throw UsageError("missing argument"); + for (int i = index; i < argc - 1; i++) + argv[i] = argv[i + 1]; + argc--; +} + +//--------------------------------------------------------- + +void fetchElement(int &dst, int index, int &argc, char *argv[]) +{ + string s = argv[index]; + if (isInt(s)) + dst = toInt(s); + else + throw UsageError("expected int"); + fetchElement(index, argc, argv); +} + +//--------------------------------------------------------- + +void fetchElement(string &dst, int index, int &argc, char *argv[]) +{ + char *s = argv[index]; + fetchElement(index, argc, argv); + if (*s == '-') + throw UsageError("expected argument"); + dst = string(s); +} + +//--------------------------------------------------------- + +} //namespace + +//========================================================= +// +// UsageElement +// +//--------------------------------------------------------- + +UsageElement::UsageElement(string name, string help) + : m_name(name), m_help(help), m_selected(false) +{ +} + +//--------------------------------------------------------- + +void UsageElement::print(ostream &out) const +{ + out << m_name; +} + +//--------------------------------------------------------- + +void UsageElement::printHelpLine(ostream &out) const +{ + out << " " << m_name << endl; + out << " " << m_help << endl; +} + +//========================================================= +// +// Qualifier +// +//--------------------------------------------------------- + +void Qualifier::print(ostream &out) const +{ + if (isSwitcher()) + out << m_name; + else + out << "[ " << m_name << " ]"; +} + +//--------------------------------------------------------- + +void SimpleQualifier::fetch(int index, int &argc, char *argv[]) +{ + fetchElement(index, argc, argv); +} + +//--------------------------------------------------------- + +void SimpleQualifier::dumpValue(ostream &out) const +{ + out << m_name << " = " << (isSelected() ? "on" : "off") << endl; +} + +//--------------------------------------------------------- + +void SimpleQualifier::resetValue() +{ + m_selected = false; +} + +//========================================================= +// +// Argument & MultiArgument +// +//--------------------------------------------------------- + +void Argument::fetch(int index, int &argc, char *argv[]) +{ + if (index >= argc) + throw UsageError("missing argument"); + if (!assign(argv[index])) + throw UsageError(string("bad argument type :") + argv[index]); + for (int i = index; i < argc - 1; i++) + argv[i] = argv[i + 1]; + argc--; +} + +//--------------------------------------------------------- + +void MultiArgument::fetch(int index, int &argc, char *argv[]) +{ + if (index >= argc) + throw UsageError("missing argument(s)"); + allocate(argc - index); + for (m_index = 0; m_index < m_count; m_index++) + if (!assign(argv[index + m_index])) + throw UsageError(string("bad argument type :") + argv[index + m_index]); + argc -= m_count; +} + +//========================================================= +// +// UsageLine +// +//--------------------------------------------------------- + +UsageLine::UsageLine() + : m_elements(0), m_count(0) +{ +} + +//--------------------------------------------------------- + +UsageLine::~UsageLine() +{ + if (m_elements) { + delete[] m_elements; + } +} + +//--------------------------------------------------------- + +UsageLine::UsageLine(const UsageLine &src) +{ + m_count = src.m_count; + m_elements = new UsageElementPtr[m_count]; + ::memcpy(m_elements, src.m_elements, m_count * sizeof(m_elements[0])); +} + +//--------------------------------------------------------- + +UsageLine &UsageLine::operator=(const UsageLine &src) +{ + if (src.m_elements != m_elements) { + delete[] m_elements; + m_elements = new UsageElementPtr[src.m_count]; + ::memcpy(m_elements, src.m_elements, src.m_count * sizeof(m_elements[0])); + } + m_count = src.m_count; + return *this; +} + +//--------------------------------------------------------- + +UsageLine::UsageLine(const UsageLine &src, UsageElement &elem) +{ + m_count = src.m_count; + m_elements = new UsageElementPtr[m_count + 1]; + ::memcpy(m_elements, src.m_elements, m_count * sizeof(m_elements[0])); + m_elements[m_count++] = &elem; +} + +//--------------------------------------------------------- + +UsageLine::UsageLine(int count) +{ + m_count = count; + m_elements = new UsageElementPtr[m_count]; + ::memset(m_elements, 0, m_count * sizeof(m_elements[0])); +} + +//--------------------------------------------------------- + +UsageLine::UsageLine(UsageElement &elem) +{ + m_count = 1; + m_elements = new UsageElementPtr[m_count]; + m_elements[0] = &elem; +} + +//--------------------------------------------------------- + +UsageLine::UsageLine(UsageElement &a, UsageElement &b) +{ + m_count = 2; + m_elements = new UsageElementPtr[m_count]; + m_elements[0] = &a; + m_elements[1] = &b; +} + +//--------------------------------------------------------- + +UsageLine TCli::UsageLine::operator+(UsageElement &elem) +{ + return UsageLine(*this, elem); +} + +//--------------------------------------------------------- + +UsageLine TCli::operator+(UsageElement &a, UsageElement &b) +{ + return UsageLine(a, b); +} + +//--------------------------------------------------------- + +UsageLine TCli::operator+(const UsageLine &a, const Optional &b) +{ + UsageLine ul(a.getCount() + b.getCount()); + int i; + for (i = 0; i < a.getCount(); i++) + ul[i] = a[i]; + for (int j = 0; j < b.getCount(); j++) + ul[i++] = b[j]; + return ul; +} + +//========================================================= + +Optional::Optional(const UsageLine &ul) + : UsageLine(ul.getCount() + 2) +{ + m_elements[0] = &bra; + m_elements[m_count - 1] = &ket; + for (int i = 0; i < ul.getCount(); i++) + m_elements[i + 1] = ul[i]; +} + +//========================================================= +// +// UsageImp +// +//--------------------------------------------------------- + +class TCli::UsageImp +{ + string m_progName; + vector m_usageLines; + std::map m_qtable; + vector m_qlist; + vector m_args; + typedef std::map::iterator qiterator; + typedef std::map::const_iterator const_qiterator; + + UsageLine *m_selectedUsageLine; + +public: + UsageImp(string progName); + ~UsageImp(){}; + void add(const UsageLine &); + void addStandardUsages(); + + void registerQualifier(Qualifier *q); + void registerQualifier(string name, Qualifier *q); + + void registerArgument(Argument *arg); + + void printUsageLine(ostream &out, const UsageLine &ul) const; + void printUsageLines(ostream &out) const; + + void print(ostream &out) const; + + void resetValues(); + void clear(); + void parse(int argc, char *argv[]); + void fetchArguments( + UsageLine &ul, int a, int b, + int &argc, char *argv[]); + bool matchSwitcher(const UsageLine &ul) const; + bool hasSwitcher(const UsageLine &ul) const; + bool matchArgCount(const UsageLine &ul, int a, int b, int argc) const; + + void dumpValues(ostream &out) const; + + void getArgCountRange(const UsageLine &ul, int a, int b, + int &min, int &max) const; + + static const int InfiniteArgCount; +}; + +//--------------------------------------------------------- + +const int UsageImp::InfiniteArgCount = 2048; + +//--------------------------------------------------------- + +UsageImp::UsageImp(string progName) + : m_progName(progName), m_selectedUsageLine(0) +{ + addStandardUsages(); +} + +//--------------------------------------------------------- + +void UsageImp::addStandardUsages() +{ + add(help); + add(release); + add(libRelease); +} + +//--------------------------------------------------------- + +void UsageImp::clear() +{ + m_usageLines.clear(); + m_qtable.clear(); + m_qlist.clear(); + m_args.clear(); + m_selectedUsageLine = 0; + addStandardUsages(); +} + +//--------------------------------------------------------- + +void UsageImp::registerArgument(Argument *arg) +{ + unsigned int i; + for (i = 0; i < m_args.size() && m_args[i] != arg; i++) { + } + if (i == m_args.size()) + m_args.push_back(arg); +} + +//--------------------------------------------------------- + +void UsageImp::registerQualifier(string name, Qualifier *q) +{ + m_qtable[name] = q; + unsigned int i; + for (i = 0; i < m_qlist.size() && m_qlist[i] != q; i++) { + } + if (i == m_qlist.size()) + m_qlist.push_back(q); +} + +//--------------------------------------------------------- + +void UsageImp::registerQualifier(Qualifier *q) +{ + string str = q->getName(); + const char *s0 = str.c_str(); + while (*s0 == ' ') + s0++; + const char *s = s0; + for (;;) { + if (*s != '-') { + assert(!"Qualifier name must start with '-'"); + } + do + s++; + while (isalnum(*s)); + if (s == s0 + 1) { + assert(!"Empty qualifier name"); + } + string name(s0, s - s0); + registerQualifier(name, q); + while (*s == ' ') + s++; + // ignoro gli eventuali parametri + while (isalnum(*s)) { + while (isalnum(*s)) + s++; + while (*s == ' ') + s++; + } + if (*s != '|') + break; + s++; + while (*s == ' ') + s++; + s0 = s; + } + if (*s != '\0') { + assert(!"Unexpected character"); + } +} + +//--------------------------------------------------------- + +void UsageImp::add(const UsageLine &ul) +{ + m_usageLines.push_back(ul); + for (int i = 0; i < ul.getCount(); i++) { + Qualifier *q = dynamic_cast(ul[i]); + if (q) + registerQualifier(q); + Argument *a = dynamic_cast(ul[i]); + if (a) + registerArgument(a); + } +} + +//--------------------------------------------------------- + +void UsageImp::printUsageLine(ostream &out, const UsageLine &ul) const +{ + out << m_progName; + for (int j = 0; j < ul.getCount(); j++) + if (!ul[j]->isHidden()) { + out << " "; + ul[j]->print(out); + } + out << endl; +} + +//--------------------------------------------------------- + +void UsageImp::printUsageLines(ostream &out) const +{ + bool first = true; + for (unsigned int i = 0; i < m_usageLines.size(); i++) { + const UsageLine &ul = m_usageLines[i]; + int j; + for (j = 0; j < ul.getCount(); j++) + if (!ul[j]->isHidden()) + break; + if (j == ul.getCount()) + continue; + out << (first ? "usage: " : " "); + printUsageLine(out, ul); + first = false; + } + out << endl; +} + +//--------------------------------------------------------- + +void UsageImp::print(ostream &out) const +{ + printUsageLines(out); + out << endl; + unsigned int i; + for (i = 0; i < m_qlist.size(); i++) + if (!m_qlist[i]->isHidden()) + m_qlist[i]->printHelpLine(out); + for (i = 0; i < m_args.size(); i++) + m_args[i]->printHelpLine(out); + out << endl; +} + +//--------------------------------------------------------- + +void UsageImp::parse(int argc, char *argv[]) +{ + resetValues(); + if (argc == 0 || argv[0] == 0) + throw UsageError("missing program name"); + m_progName = string(argv[0]); + unsigned int i; + for (i = 1; i < (unsigned int)argc;) + if (argv[i][0] == '-') { + string qname(argv[i]); + qiterator qit = m_qtable.find(qname); + if (qit == m_qtable.end()) { + string msg = "unknown qualifier '" + qname + "'"; + throw UsageError(msg); + } + Qualifier *qualifier = qit->second; + qualifier->fetch(i, argc, argv); + qualifier->select(); + } else + i++; + + vector usages; + + for (i = 0; i < m_usageLines.size(); i++) + if (hasSwitcher(m_usageLines[i]) && + matchSwitcher(m_usageLines[i])) + usages.push_back(&m_usageLines[i]); + if (usages.empty()) + for (i = 0; i < m_usageLines.size(); i++) + if (!hasSwitcher(m_usageLines[i])) + usages.push_back(&m_usageLines[i]); + if (usages.size() == 0) + throw UsageError("unrecognized syntax"); + if (usages.size() > 1) { + int min = InfiniteArgCount; + int max = 0; + std::vector::iterator it; + for (it = usages.begin(); it != usages.end();) { + UsageLine &ul = **it; + int lmin, lmax; + getArgCountRange(ul, 0, ul.getCount() - 1, lmin, lmax); + min = tmin(min, lmin); + max = tmax(max, lmax); + if (matchArgCount(ul, 0, ul.getCount() - 1, argc - 1) == false) + it = usages.erase(it); + else + it++; + } + if (usages.size() == 0) { + if (argc - 1 < min) + throw UsageError("missing argument(s)"); + else if (argc - 1 > max) + throw UsageError("too many arguments"); + else + throw UsageError("bad argument number"); + } else if (usages.size() > 1) + throw UsageError("ambiguous syntax"); + } + + if (usages.size() == 1) { + UsageLine *found = usages[0]; + if (found->getCount() > 0) + fetchArguments(*found, 0, found->getCount() - 1, argc, argv); + if (argc > 1) + throw UsageError("too many arguments"); + m_selectedUsageLine = found; + } +} + +//--------------------------------------------------------- + +void UsageImp::fetchArguments( + UsageLine &ul, + int a, int b, int &argc, char *argv[]) +{ + assert(0 <= a && a <= b && b < ul.getCount()); + int i; + for (i = a; i <= b; i++) { + if (ul[i] == &bra || ul[i]->isMultiArgument()) + break; + if (ul[i]->isArgument()) { + Argument *argument = dynamic_cast(ul[i]); + assert(argument); + argument->fetch(1, argc, argv); + argument->select(); + } + } + if (i > b) { + } else if (ul[i] == &bra) { + int n = 0; + int j; + for (j = b; j > i && ul[j] != &ket; j--) + if (ul[j]->isArgument()) + n++; + assert(j > i + 1); + if (argc - 1 > n) + fetchArguments(ul, i + 1, j - 1, argc, argv); + if (j + 1 <= b) + fetchArguments(ul, j + 1, b, argc, argv); + } else if (ul[i]->isMultiArgument()) { + MultiArgument *argument = dynamic_cast(ul[i]); + assert(argument); + int n = 0; + for (int j = i + 1; j <= b; j++) + if (ul[j]->isArgument()) + n++; + int oldArgc = argc; + argc -= n; + argument->fetch(1, argc, argv); + argument->select(); + argc += n; + if (n > 0) { + if (argc < oldArgc) + for (int j = n; j > 0; j--) + argv[argc - j] = argv[oldArgc - j]; + fetchArguments(ul, i + 1, b, argc, argv); + } + } +} + +//--------------------------------------------------------- + +bool UsageImp::matchSwitcher(const UsageLine &ul) const +{ + for (int i = 0; i < ul.getCount(); i++) + if (ul[i]->isSwitcher() && + !ul[i]->isSelected()) + return false; + return true; +} + +//--------------------------------------------------------- + +bool UsageImp::hasSwitcher(const UsageLine &ul) const +{ + for (int i = 0; i < ul.getCount(); i++) + if (ul[i]->isSwitcher()) + return true; + return false; +} + +//--------------------------------------------------------- + +bool UsageImp::matchArgCount(const UsageLine &ul, + int a, int b, int argc) const +{ + int n = 0; + int i; + for (i = a; i <= b; i++) { + if (ul[i] == &bra || ul[i]->isMultiArgument()) + break; + if (ul[i]->isArgument()) + n++; + } + if (i > b) + return argc == n; + else if (ul[i] == &bra) { + int j; + for (j = b; j > i && ul[j] != &ket; j--) + if (ul[j]->isArgument()) + n++; + assert(j > i + 1); + if (n == argc) + return true; + else if (n > argc) + return false; + else + return matchArgCount(ul, i + 1, j - 1, argc - n); + } else { + assert(ul[i]->isMultiArgument()); + n++; + for (i++; i <= b; i++) + if (ul[i]->isArgument()) + n++; + return argc >= n; + } +} + +//--------------------------------------------------------- + +void UsageImp::getArgCountRange(const UsageLine &ul, int a, int b, + int &min, int &max) const +{ + min = max = 0; + int n = 0; + int i; + for (i = a; i <= b; i++) { + if (ul[i] == &bra || ul[i]->isMultiArgument()) + break; + if (ul[i]->isArgument()) + n++; + } + if (i > b) { + min = max = n; + return; + } else if (ul[i] == &bra) { + int j; + for (j = b; j > i && ul[j] != &ket; j--) + if (ul[j]->isArgument()) + n++; + assert(j > i + 1); + min = max = n; + int lmin, lmax; + getArgCountRange(ul, i + 1, j - 1, lmin, lmax); + if (lmax == InfiniteArgCount) + max = InfiniteArgCount; + else + max += lmax; + } else { + assert(ul[i]->isMultiArgument()); + n++; + for (i++; i <= b; i++) + if (ul[i]->isArgument()) + n++; + min = n; + max = InfiniteArgCount; + } +} + +//--------------------------------------------------------- + +void UsageImp::dumpValues(ostream &out) const +{ + if (m_selectedUsageLine == 0) + return; + cout << "selected usage: "; + printUsageLine(out, *m_selectedUsageLine); + unsigned int i; + for (i = 0; i < m_qlist.size(); i++) + m_qlist[i]->dumpValue(out); + for (i = 0; i < m_args.size(); i++) + m_args[i]->dumpValue(out); + out << endl + << endl; +} + +//--------------------------------------------------------- + +void UsageImp::resetValues() +{ + unsigned int i; + for (i = 0; i < m_qlist.size(); i++) + m_qlist[i]->resetValue(); + for (i = 0; i < m_args.size(); i++) + m_args[i]->resetValue(); +} + +//========================================================= +// +// Usage +// +//--------------------------------------------------------- + +Usage::Usage(string progName) +{ + m_imp = new UsageImp(progName); +} + +Usage::~Usage() +{ + delete m_imp; +} + +void Usage::add(const UsageLine &ul) +{ + m_imp->add(ul); +} + +void Usage::print(ostream &out) const +{ + m_imp->print(out); +} + +void Usage::dumpValues(ostream &out) const +{ + m_imp->dumpValues(out); +} + +void Usage::clear() +{ + m_imp->clear(); +} + +bool Usage::parse(const char *argvString, ostream &err) +{ + string s = string(argvString); + std::vector argv; + int len = s.size(); + for (int i = 0; i < len;) { + while (s[i] == ' ' || s[i] == '\t') + i++; + if (i < len) + argv.push_back(&s[i]); + while (i < len && !(s[i] == ' ' || s[i] == '\t')) + i++; + if (i < len) + s[i++] = '\0'; + } + return parse(argv.size(), &argv[0], err); +} + +bool Usage::parse(int argc, char *argv[], ostream &err) +{ + try { + m_imp->parse(argc, argv); + if (help) { + print(err); + return false; + } + if (release) { + printToonzRelease(err); + return false; + } + if (libRelease) { + printLibRelease(err); + return false; + } + return true; + } catch (UsageError e) { + err << "Usage error: " << e.getError() << endl; + m_imp->printUsageLines(err); + return false; + } +} + +//========================================================= +// +// RangeQualifier +// +//--------------------------------------------------------- + +RangeQualifier::RangeQualifier() + : Qualifier("-range from to | -frame fr", + "frame range"), + m_from(0), m_to(-1) +{ +} + +//--------------------------------------------------------- + +void RangeQualifier::fetch(int index, int &argc, char *argv[]) +{ + assert(index < argc); + string qname(argv[index]); + fetchElement(index, argc, argv); + if (qname == "-range") { + fetchElement(m_from, index, argc, argv); + fetchElement(m_to, index, argc, argv); + } else if (qname == "-frame") { + fetchElement(m_from, index, argc, argv); + m_to = m_from; + } else { + assert(0); + } +} + +ostream &operator<<(ostream &out, const RangeQualifier &range) +{ + return out << "[" << range.getFrom() << ", " << range.getTo() << "]"; +} + +//--------------------------------------------------------- + +void RangeQualifier::dumpValue(ostream &out) const +{ + out << m_name << " = "; + if (m_from <= m_to) + out << m_from << ", " << m_to; + else + out << "undefined"; + out << endl; +} + +//--------------------------------------------------------- + +void RangeQualifier::resetValue() +{ + m_from = 0; + m_to = -1; + m_selected = false; +} diff --git a/toonz/sources/common/tapptools/tcolorutils.cpp b/toonz/sources/common/tapptools/tcolorutils.cpp new file mode 100644 index 0000000..e2075b3 --- /dev/null +++ b/toonz/sources/common/tapptools/tcolorutils.cpp @@ -0,0 +1,911 @@ + + +//#include "traster.h" +#include "tcolorutils.h" +#include "tmathutil.h" + +#include +#include + +typedef float KEYER_FLOAT; + +//------------------------------------------------------------------------------ +#ifdef WIN32 +#define ISNAN _isnan +#else +extern "C" int isnan(double); +#define ISNAN isnan +#endif + +//------------------------------------------------------------------------------ + +//#define CLUSTER_ELEM_CONTAINER_IS_A_SET +//#define WITH_ALPHA_IN_STATISTICS + +//------------------------------------------------------------------------------ + +class ClusterStatistic +{ +public: + KEYER_FLOAT sumComponents[3]; // vettore 3x1 + unsigned int elemsCount; + KEYER_FLOAT matrixR[9]; // matrice 3x3 = somma(x * trasposta(x)) + // dove x sono i pixel del cluster + + KEYER_FLOAT covariance[9]; // matrice di covarianza + TPoint sumCoords; + +#ifdef WITH_ALPHA_IN_STATISTICS + + KEYER_FLOAT sumAlpha; + +#endif +}; + +//------------------------------------------------------------------------------ + +class ClusterElem +{ +public: + ClusterElem(unsigned char _r, + unsigned char _g, + unsigned char _b, + KEYER_FLOAT _a, + unsigned int _x = 0, + unsigned int _y = 0) + : r(toDouble(_r)), g(toDouble(_g)), b(toDouble(_b)), a(_a), x(_x), y(_y), pix32(TPixel32(_r, _g, _b)) + { + } + + ~ClusterElem() {} + + static KEYER_FLOAT toDouble(unsigned char chan) + { + return ((KEYER_FLOAT)chan) * (KEYER_FLOAT)(1.0 / 255.0); + } + + unsigned int x; + unsigned int y; + KEYER_FLOAT r; + KEYER_FLOAT g; + KEYER_FLOAT b; + KEYER_FLOAT a; + TPixel32 pix32; +}; + +//------------------------------------------------------------------------------ + +#ifdef CLUSTER_ELEM_CONTAINER_IS_A_SET + +typedef std::set ClusterElemContainer; + +#else + +typedef std::vector ClusterElemContainer; + +#endif + +//------------------------------------------------------------------------------ + +class Cluster +{ +public: + Cluster(); + Cluster(const Cluster &rhs); + + ~Cluster(); + + void computeCovariance(); + void insert(ClusterElem *elem); + void computeStatistics(); + void getMeanAxis(KEYER_FLOAT axis[3]); + + ClusterStatistic statistic; + ClusterElemContainer data; + + KEYER_FLOAT eigenVector[3]; + KEYER_FLOAT eigenValue; + +private: + void operator=(const Cluster &); +}; + +//------------------------------------------------------------------------------ + +typedef std::vector ClusterContainer; + +//---------------------------------------------------------------------------- + +void chooseLeafToClusterize(ClusterContainer::iterator &itRet, + KEYER_FLOAT &eigenValue, + KEYER_FLOAT eigenVector[3], + ClusterContainer &clusters); + +void split(Cluster *subcluster1, + Cluster *subcluster2, + KEYER_FLOAT eigenVector[3], + Cluster *cluster); + +void SolveCubic(KEYER_FLOAT a, /* coefficient of x^3 */ + KEYER_FLOAT b, /* coefficient of x^2 */ + KEYER_FLOAT c, /* coefficient of x */ + KEYER_FLOAT d, /* constant term */ + int *solutions, /* # of distinct solutions */ + KEYER_FLOAT *x); /* array of solutions */ + +unsigned short int calcCovarianceEigenValues(const KEYER_FLOAT covariance[9], + KEYER_FLOAT eigenValues[3]); + +//---------------------------------------------------------------------------- + +void split(Cluster *subcluster1, + Cluster *subcluster2, + KEYER_FLOAT eigenVector[3], + Cluster *cluster) +{ + KEYER_FLOAT n = (KEYER_FLOAT)cluster->statistic.elemsCount; + + KEYER_FLOAT mean[3]; + mean[0] = cluster->statistic.sumComponents[0] / n; + mean[1] = cluster->statistic.sumComponents[1] / n; + mean[2] = cluster->statistic.sumComponents[2] / n; + + ClusterElemContainer::const_iterator it = cluster->data.begin(); + for (; it != cluster->data.end(); ++it) { + ClusterElem *elem = *it; + + KEYER_FLOAT r = (KEYER_FLOAT)elem->r; + KEYER_FLOAT g = (KEYER_FLOAT)elem->g; + KEYER_FLOAT b = (KEYER_FLOAT)elem->b; + + //cluster->data.erase(it); + + if (eigenVector[0] * r + eigenVector[1] * g + eigenVector[2] * b <= + eigenVector[0] * mean[0] + eigenVector[1] * mean[1] + eigenVector[2] * mean[2]) + subcluster1->insert(elem); + else + subcluster2->insert(elem); + } +} + +//---------------------------------------------------------------------------- + +void chooseLeafToClusterize(ClusterContainer::iterator &itRet, + KEYER_FLOAT &eigenValue, + KEYER_FLOAT eigenVector[3], + ClusterContainer &clusters) +{ + itRet = clusters.end(); + + ClusterContainer::iterator itFound = clusters.end(); + bool found = false; + KEYER_FLOAT maxEigenValue = 0.0; + + unsigned short int multeplicity = 0; + + ClusterContainer::iterator it = clusters.begin(); + for (; it != clusters.end(); ++it) { + unsigned short int tmpMulteplicity = 0; + KEYER_FLOAT tmpEigenValue; + + Cluster *cluster = *it; + // calcola la matrice di covarianza + const KEYER_FLOAT *clusterCovariance = cluster->statistic.covariance; + assert(!ISNAN(clusterCovariance[0])); + + // calcola gli autovalori della matrice di covarianza della statistica + // del cluster (siccome la matrice e' simmetrica gli autovalori + // sono tutti reali) + KEYER_FLOAT eigenValues[3]; + tmpMulteplicity = calcCovarianceEigenValues(clusterCovariance, eigenValues); + assert(tmpMulteplicity > 0); + + tmpEigenValue = tmax(eigenValues[0], eigenValues[1], eigenValues[2]); + cluster->eigenValue = tmpEigenValue; + + // eventuale aggiornamento del cluster da cercare + if (itFound == clusters.end()) { + itFound = it; + maxEigenValue = tmpEigenValue; + multeplicity = tmpMulteplicity; + found = true; + } else { + if (tmpEigenValue > maxEigenValue) { + itFound = it; + maxEigenValue = tmpEigenValue; + multeplicity = tmpMulteplicity; + } + } + } + + if (found) { + assert(multeplicity > 0); + itRet = itFound; + eigenValue = maxEigenValue; + + // calcola l'autovettore relativo a maxEigenValue + Cluster *clusterFound = *itFound; + + assert(multeplicity > 0); + + KEYER_FLOAT tmpMatrixM[9]; + + const KEYER_FLOAT *clusterCovariance = clusterFound->statistic.covariance; + int i = 0; + for (; i < 9; ++i) + tmpMatrixM[i] = clusterCovariance[i]; + + tmpMatrixM[0] -= maxEigenValue; + tmpMatrixM[4] -= maxEigenValue; + tmpMatrixM[8] -= maxEigenValue; + + for (i = 0; i < 3; ++i) + eigenVector[i] = 0.0; + + if (multeplicity == 1) { + KEYER_FLOAT u11 = tmpMatrixM[4] * tmpMatrixM[8] - tmpMatrixM[5] * tmpMatrixM[5]; + KEYER_FLOAT u12 = tmpMatrixM[2] * tmpMatrixM[5] - tmpMatrixM[1] * tmpMatrixM[8]; + KEYER_FLOAT u13 = tmpMatrixM[1] * tmpMatrixM[5] - tmpMatrixM[2] * tmpMatrixM[5]; + KEYER_FLOAT u22 = tmpMatrixM[0] * tmpMatrixM[8] - tmpMatrixM[2] * tmpMatrixM[2]; + KEYER_FLOAT u23 = tmpMatrixM[1] * tmpMatrixM[2] - tmpMatrixM[5] * tmpMatrixM[0]; + KEYER_FLOAT u33 = tmpMatrixM[0] * tmpMatrixM[4] - tmpMatrixM[1] * tmpMatrixM[1]; + + KEYER_FLOAT uMax = tmax(tmax(u11, u12, u13), u22, u23, u33); + + if (uMax == u11) { + eigenVector[0] = u11; + eigenVector[1] = u12; + eigenVector[2] = u13; + } else if (uMax == u12) { + eigenVector[0] = u12; + eigenVector[1] = u22; + eigenVector[2] = u23; + } else if (uMax == u13) { + eigenVector[0] = u13; + eigenVector[1] = u23; + eigenVector[2] = u33; + } else if (uMax == u22) { + eigenVector[0] = u12; + eigenVector[1] = u22; + eigenVector[2] = u23; + } else if (uMax == u23) { + eigenVector[0] = u13; + eigenVector[1] = u23; + eigenVector[2] = u33; + } else if (uMax == u33) { + eigenVector[0] = u13; + eigenVector[1] = u23; + eigenVector[2] = u33; + } else { + assert(false && "impossibile!!"); + } + + } else if (multeplicity == 2) { + short int row = -1; + short int col = -1; + + KEYER_FLOAT mMax = tmax(tmax(tmpMatrixM[0], tmpMatrixM[1], tmpMatrixM[2]), + tmpMatrixM[4], tmpMatrixM[5], tmpMatrixM[8]); + + if (mMax == tmpMatrixM[0]) { + row = 1; + col = 1; + } else if (mMax == tmpMatrixM[1]) { + row = 1; + col = 2; + } else if (mMax == tmpMatrixM[2]) { + row = 1; + col = 3; + } else if (mMax == tmpMatrixM[4]) { + row = 2; + col = 2; + } else if (mMax == tmpMatrixM[5]) { + row = 2; + col = 3; + } else if (mMax == tmpMatrixM[8]) { + row = 3; + col = 3; + } + + if (row == 1) { + if (col == 1 || col == 2) { + eigenVector[0] = -tmpMatrixM[1]; + eigenVector[1] = tmpMatrixM[0]; + eigenVector[2] = 0.0; + } else { + eigenVector[0] = tmpMatrixM[2]; + eigenVector[1] = 0.0; + eigenVector[2] = -tmpMatrixM[0]; + } + } else if (row == 2) { + eigenVector[0] = 0.0; + eigenVector[1] = -tmpMatrixM[5]; + eigenVector[2] = tmpMatrixM[4]; + } else if (row == 3) { + eigenVector[0] = 0.0; + eigenVector[1] = -tmpMatrixM[8]; + eigenVector[2] = tmpMatrixM[5]; + } + } else if (multeplicity == 3) { + eigenVector[0] = 1.0; + eigenVector[1] = 0.0; + eigenVector[2] = 0.0; + } else { + assert(false && "impossibile!!"); + } + + // normalizzazione dell'autovettore calcolato + /* + KEYER_FLOAT eigenVectorMagnitude = sqrt(eigenVector[0]*eigenVector[0] + + eigenVector[1]*eigenVector[1] + + eigenVector[2]*eigenVector[2]); + assert(eigenVectorMagnitude > 0); + + eigenVector[0] /= eigenVectorMagnitude; + eigenVector[1] /= eigenVectorMagnitude; + eigenVector[2] /= eigenVectorMagnitude; + */ + + clusterFound->eigenVector[0] = eigenVector[0]; + clusterFound->eigenVector[1] = eigenVector[1]; + clusterFound->eigenVector[2] = eigenVector[2]; + + assert(!ISNAN(eigenVector[0])); + assert(!ISNAN(eigenVector[1])); + assert(!ISNAN(eigenVector[2])); + } +} + +//---------------------------------------------------------------------------- + +unsigned short int calcCovarianceEigenValues(const KEYER_FLOAT clusterCovariance[9], + KEYER_FLOAT eigenValues[3]) +{ + unsigned short int multeplicity = 0; + + KEYER_FLOAT a11 = clusterCovariance[0]; + KEYER_FLOAT a12 = clusterCovariance[1]; + KEYER_FLOAT a13 = clusterCovariance[2]; + KEYER_FLOAT a22 = clusterCovariance[4]; + KEYER_FLOAT a23 = clusterCovariance[5]; + KEYER_FLOAT a33 = clusterCovariance[8]; + + KEYER_FLOAT c0 = + (KEYER_FLOAT)(a11 * a22 * a33 + 2.0 * a12 * a13 * a23 - a11 * a23 * a23 - a22 * a13 * a13 - a33 * a12 * a12); + + KEYER_FLOAT c1 = (KEYER_FLOAT)(a11 * a22 - a12 * a12 + a11 * a33 - a13 * a13 + a22 * a33 - a23 * a23); + + KEYER_FLOAT c2 = (KEYER_FLOAT)(a11 + a22 + a33); + + int solutionsCount = 0; + SolveCubic((KEYER_FLOAT)-1.0, c2, -c1, c0, &solutionsCount, eigenValues); + assert(solutionsCount > 0); + multeplicity = 4 - solutionsCount; + + assert(!ISNAN(eigenValues[0])); + assert(!ISNAN(eigenValues[1])); + assert(!ISNAN(eigenValues[2])); + + assert(multeplicity > 0); + return multeplicity; +} + +//---------------------------------------------------------------------------- + +void SolveCubic(KEYER_FLOAT a, /* coefficient of x^3 */ + KEYER_FLOAT b, /* coefficient of x^2 */ + KEYER_FLOAT c, /* coefficient of x */ + KEYER_FLOAT d, /* constant term */ + int *solutions, /* # of distinct solutions */ + KEYER_FLOAT *x) /* array of solutions */ +{ + static const KEYER_FLOAT epsilon = (KEYER_FLOAT)0.0001; + if (a != 0 && fabs(b - 0.0) <= epsilon && fabs(c - 0.0) <= epsilon && fabs(d - 0.0) <= epsilon) + //if(a != 0 && b == 0 && c == 0 && d == 0) + { + *solutions = 1; + x[0] = x[1] = x[2] = 0.0; + return; + } + KEYER_FLOAT a1 = (KEYER_FLOAT)(b / a); + KEYER_FLOAT a2 = (KEYER_FLOAT)(c / a); + KEYER_FLOAT a3 = (KEYER_FLOAT)(d / a); + KEYER_FLOAT Q = (KEYER_FLOAT)((a1 * a1 - 3.0 * a2) / 9.0); + KEYER_FLOAT R = (KEYER_FLOAT)((2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0); + KEYER_FLOAT R2_Q3 = (KEYER_FLOAT)(R * R - Q * Q * Q); + KEYER_FLOAT theta; + KEYER_FLOAT PI = (KEYER_FLOAT)3.1415926535897932384626433832795; + + if (R2_Q3 <= 0) { + *solutions = 3; + theta = (KEYER_FLOAT)acos(R / sqrt(Q * Q * Q)); + x[0] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos(theta / 3.0) - a1 / 3.0); + x[1] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos((theta + 2.0 * PI) / 3.0) - a1 / 3.0); + x[2] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos((theta + 4.0 * PI) / 3.0) - a1 / 3.0); + + assert(!ISNAN(x[0])); + assert(!ISNAN(x[1])); + assert(!ISNAN(x[2])); + + /* + long KEYER_FLOAT v; + v = x[0]; + assert(areAlmostEqual(a*v*v*v+b*v*v+c*v+d, 0.0)); + v = x[1]; + assert(areAlmostEqual(a*v*v*v+b*v*v+c*v+d, 0.0)); + v = x[2]; + assert(areAlmostEqual(a*v*v*v+b*v*v+c*v+d, 0.0)); + */ + } else { + *solutions = 1; + x[0] = (KEYER_FLOAT)pow((float)(sqrt(R2_Q3) + fabs(R)), (float)(1 / 3.0)); + x[0] += (KEYER_FLOAT)(Q / x[0]); + x[0] *= (KEYER_FLOAT)((R < 0.0) ? 1 : -1); + x[0] -= (KEYER_FLOAT)(a1 / 3.0); + + assert(!ISNAN(x[0])); + + /* + long KEYER_FLOAT v; + v = x[0]; + assert(areAlmostEqual(a*v*v*v+b*v*v+c*v+d, 0.0)); + */ + } +} + +//---------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ + +void clusterize(ClusterContainer &clusters, int clustersCount) +{ + unsigned int clustersSize = clusters.size(); + assert(clustersSize >= 1); + + // faccio in modo che in clusters ci siano solo e sempre le foglie + // dell'albero calcolato secondo l'algoritmo TSE descritto da Orchard-Bouman + // (c.f.r. "Color Quantization of Images" - M.Orchard, C. Bouman) + + // numero di iterazioni, numero di cluster = numero di iterazioni + 1 + int m = clustersCount - 1; + int i = 0; + for (; i < m; ++i) { + // sceglie la foglia dell'albero dei cluster (ovvero il cluster nel + // ClusterContainer "clusters") che ha il maggiore autovalore, ovvero + // il cluster che ha maggiore varainza rispetto all'asse opportuno + // (che poi e' l'autovettore corrispondente all'autovalore piu' grande) + KEYER_FLOAT eigenValue = 0.0; + KEYER_FLOAT eigenVector[3] = {0.0, 0.0, 0.0}; + ClusterContainer::iterator itChoosedCluster; + + chooseLeafToClusterize(itChoosedCluster, + eigenValue, + eigenVector, + clusters); + + assert(itChoosedCluster != clusters.end()); + Cluster *choosedCluster = *itChoosedCluster; + +#if 0 + + // se il cluster che si e' scelto per la suddivisione contiene un solo + // elemento vuol dire che non c'e' piu' niente da suddividere e si esce + // dal ciclo. + // Questo succede quando si sono chiesti piu' clusters di quanti elementi + // ci sono nel cluster iniziale. + if(choosedCluster->statistic.elemsCount == 1) + break; + +#else + + // un cluster che ha un solo elemento non ha molto senso di esistere, + // credo crei problemi anche nel calcolo della matrice di covarianza, + // quindi mi fermo quando il cluster contiene meno di 4 elementi + if (choosedCluster->statistic.elemsCount == 3) + break; + +#endif + + // suddivide il cluster scelto in altri due cluster + Cluster *subcluster1 = new Cluster(); + Cluster *subcluster2 = new Cluster(); + split(subcluster1, subcluster2, eigenVector, choosedCluster); + assert(subcluster1); + assert(subcluster2); + if ((subcluster1->data.size() == 0) || + (subcluster2->data.size() == 0)) + break; + + // calcola la nuova statistica per subcluster1 + subcluster1->computeStatistics(); + + // calcola la nuova statistica per subcluster2 + int j = 0; + for (; j < 3; ++j) { + subcluster2->statistic.sumComponents[j] = + choosedCluster->statistic.sumComponents[j] - + subcluster1->statistic.sumComponents[j]; + } + + subcluster2->statistic.sumCoords.x = + choosedCluster->statistic.sumCoords.x - + subcluster1->statistic.sumCoords.x; + + subcluster2->statistic.sumCoords.y = + choosedCluster->statistic.sumCoords.y - + subcluster1->statistic.sumCoords.y; + + subcluster2->statistic.elemsCount = + choosedCluster->statistic.elemsCount - + subcluster1->statistic.elemsCount; + +#ifdef WITH_ALPHA_IN_STATISTICS + + subcluster2->statistic.sumAlpha = + choosedCluster->statistic.sumAlpha - + subcluster1->statistic.sumAlpha; + +#endif + + for (j = 0; j < 9; ++j) + subcluster2->statistic.matrixR[j] = + choosedCluster->statistic.matrixR[j] - + subcluster1->statistic.matrixR[j]; + + subcluster2->computeCovariance(); + + // aggiorna in modo opportuno il ClusterContainer "clusters", cancellando + // il cluster scelto e inserendo i due appena creati. + // Facendo cosi' il ClusterContainer "cluster" contiene solo e sempre + // le foglie dell'albero creato dall'algoritmo TSE. + + Cluster *cluster = *itChoosedCluster; + assert(cluster); + cluster->data.clear(); + //clearPointerContainer(cluster->data); + assert(cluster->data.size() == 0); + delete cluster; + clusters.erase(itChoosedCluster); + + clusters.push_back(subcluster1); + clusters.push_back(subcluster2); + } +} + +//------------------------------------------------------------------------------ + +Cluster::Cluster() +{ +} + +//------------------------------------------------------------------------------ + +Cluster::Cluster(const Cluster &rhs) + : statistic(rhs.statistic) +{ + ClusterElemContainer::const_iterator it = rhs.data.begin(); + for (; it != rhs.data.end(); ++it) + data.push_back(new ClusterElem(**it)); +} + +//------------------------------------------------------------------------------ + +Cluster::~Cluster() +{ + clearPointerContainer(data); +} + +//------------------------------------------------------------------------------ + +void Cluster::computeCovariance() +{ + KEYER_FLOAT sumComponentsMatrix[9]; + + KEYER_FLOAT sumR = statistic.sumComponents[0]; + KEYER_FLOAT sumG = statistic.sumComponents[1]; + KEYER_FLOAT sumB = statistic.sumComponents[2]; + + sumComponentsMatrix[0] = sumR * sumR; + sumComponentsMatrix[1] = sumR * sumG; + sumComponentsMatrix[2] = sumR * sumB; + + sumComponentsMatrix[3] = sumComponentsMatrix[1]; + sumComponentsMatrix[4] = sumG * sumG; + sumComponentsMatrix[5] = sumG * sumB; + + sumComponentsMatrix[6] = sumComponentsMatrix[2]; + sumComponentsMatrix[7] = sumComponentsMatrix[5]; + sumComponentsMatrix[8] = sumB * sumB; + + KEYER_FLOAT n = (KEYER_FLOAT)statistic.elemsCount; + assert(n > 0); + int i = 0; + for (; i < 9; ++i) { + statistic.covariance[i] = + statistic.matrixR[i] - sumComponentsMatrix[i] / n; + assert(!ISNAN(statistic.matrixR[i])); + //assert(statistic.covariance[i] >= 0.0); + // instabilita' numerica ??? + if (statistic.covariance[i] < 0.0) + statistic.covariance[i] = 0.0; + } +} + +//------------------------------------------------------------------------------ + +void Cluster::insert(ClusterElem *elem) +{ + +#ifdef CLUSTER_ELEM_CONTAINER_IS_A_SET + + data.insert(elem); + +#else + + data.push_back(elem); + +#endif +} + +//------------------------------------------------------------------------------ + +void Cluster::computeStatistics() +{ + // inizializza a zero la statistica del cluster + statistic.elemsCount = 0; + + statistic.sumCoords = TPoint(0, 0); + + int i = 0; + for (; i < 3; ++i) + statistic.sumComponents[i] = 0.0; + + for (i = 0; i < 9; ++i) + statistic.matrixR[i] = 0.0; + + // calcola la statistica del cluster + ClusterElemContainer::const_iterator it = data.begin(); + for (; it != data.end(); ++it) { + const ClusterElem *elem = *it; + +#ifdef WITH_ALPHA_IN_STATISTICS + KEYER_FLOAT alpha = elem->a; +#endif + KEYER_FLOAT r = (KEYER_FLOAT)elem->r; + KEYER_FLOAT g = (KEYER_FLOAT)elem->g; + KEYER_FLOAT b = (KEYER_FLOAT)elem->b; + + statistic.sumComponents[0] += r; + statistic.sumComponents[1] += g; + statistic.sumComponents[2] += b; + +#ifdef WITH_ALPHA_IN_STATISTICS + + statistic.sumAlpha += alpha; + +#endif + + // prima riga della matrice R + statistic.matrixR[0] += r * r; + statistic.matrixR[1] += r * g; + statistic.matrixR[2] += r * b; + + // seconda riga della matrice R + statistic.matrixR[3] += r * g; + statistic.matrixR[4] += g * g; + statistic.matrixR[5] += g * b; + + // terza riga della matrice R + statistic.matrixR[6] += r * b; + statistic.matrixR[7] += b * g; + statistic.matrixR[8] += b * b; + + statistic.sumCoords.x += elem->x; + statistic.sumCoords.y += elem->y; + + ++statistic.elemsCount; + } + + assert(statistic.elemsCount > 0); + + computeCovariance(); +} + +//------------------------------------------------------------------------------ + +void Cluster::getMeanAxis(KEYER_FLOAT axis[3]) +{ + KEYER_FLOAT n = (KEYER_FLOAT)statistic.elemsCount; + +#if 1 + + axis[0] = (KEYER_FLOAT)(sqrt(statistic.covariance[0]) / n); + axis[1] = (KEYER_FLOAT)(sqrt(statistic.covariance[4]) / n); + axis[2] = (KEYER_FLOAT)(sqrt(statistic.covariance[8]) / n); + +#else + + KEYER_FLOAT I[3]; + KEYER_FLOAT J[3]; + KEYER_FLOAT K[3]; + + I[0] = statistic.covariance[0]; + I[1] = statistic.covariance[1]; + I[2] = statistic.covariance[2]; + + J[0] = statistic.covariance[3]; + J[1] = statistic.covariance[4]; + J[2] = statistic.covariance[5]; + + K[0] = statistic.covariance[6]; + K[1] = statistic.covariance[7]; + K[2] = statistic.covariance[8]; + + KEYER_FLOAT magnitudeI = I[0] * I[0] + I[1] * I[1] + I[2] * I[2]; + KEYER_FLOAT magnitudeJ = J[0] * J[0] + J[1] * J[1] + J[2] * I[2]; + KEYER_FLOAT magnitudeK = K[0] * K[0] + K[1] * K[1] + K[2] * I[2]; + + if (magnitudeI >= magnitudeJ && magnitudeI >= magnitudeK) { + axis[0] = sqrt(I[0] / n); + axis[1] = sqrt(I[1] / n); + axis[2] = sqrt(I[2] / n); + } else if (magnitudeJ >= magnitudeI && magnitudeJ >= magnitudeK) { + axis[0] = sqrt(J[0] / n); + axis[1] = sqrt(J[1] / n); + axis[2] = sqrt(J[2] / n); + } else if (magnitudeK >= magnitudeI && magnitudeK >= magnitudeJ) { + axis[0] = sqrt(K[0] / n); + axis[1] = sqrt(K[1] / n); + axis[2] = sqrt(K[2] / n); + } + +#endif +} + +//------------------------------------------------------------------------------ + +//#define METODO_USATO_SU_TOONZ46 + +void buildPaletteForBlendedImages(std::set &palette, const TRaster32P &raster, int maxColorCount) +{ + int lx = raster->getLx(); + int ly = raster->getLy(); + + ClusterContainer clusters; + Cluster *cluster = new Cluster; + raster->lock(); + for (int y = 0; y < ly; ++y) { + TPixel32 *pix = raster->pixels(y); + for (int x = 0; x < lx; ++x) { + TPixel32 color = *(pix + x); + ClusterElem *ce = new ClusterElem(color.r, color.g, color.b, (float)(color.m / 255.0)); + cluster->insert(ce); + } + } + raster->unlock(); + cluster->computeStatistics(); + + clusters.push_back(cluster); + clusterize(clusters, maxColorCount); + + palette.clear(); + //palette.reserve( clusters.size()); + + for (UINT i = 0; i < clusters.size(); ++i) { + ClusterStatistic &stat = clusters[i]->statistic; + TPixel32 col((int)(stat.sumComponents[0] / stat.elemsCount * 255), + (int)(stat.sumComponents[1] / stat.elemsCount * 255), + (int)(stat.sumComponents[2] / stat.elemsCount * 255), + 255); + palette.insert(col); + + clearPointerContainer(clusters[i]->data); + } + + clearPointerContainer(clusters); +} + +//------------------------------------------------------------------------------ + +namespace +{ +#define DISTANCE 3 + +bool inline areNear(const TPixel &c1, const TPixel &c2) +{ + if (abs(c1.r - c2.r) > DISTANCE) + return false; + if (abs(c1.g - c2.g) > DISTANCE) + return false; + if (abs(c1.b - c2.b) > DISTANCE) + return false; + if (abs(c1.m - c2.m) > DISTANCE) + return false; + + return true; +} + +bool find(const std::set &palette, const TPixel &color) +{ + std::set::const_iterator it = palette.begin(); + + while (it != palette.end()) { + if (areNear(*it, color)) + return true; + ++it; + } + + return false; +} + +} //namespace + +/*-- 似ている色をまとめて1つのStyleにする --*/ +void TColorUtils::buildPalette(std::set &palette, const TRaster32P &raster, int maxColorCount) +{ + int lx = raster->getLx(); + int ly = raster->getLy(); + int wrap = raster->getWrap(); + + int x, y; + TPixel old = TPixel::Black; + int solidColors = 0; + int count = maxColorCount; + raster->lock(); + for (y = 1; y < ly - 1 && count > 0; y++) { + TPixel *pix = raster->pixels(y); + for (x = 1; x < lx - 1 && count > 0; x++, pix++) { + TPixel color = *pix; + if (areNear(color, *(pix - 1)) && areNear(color, *(pix + 1)) && + areNear(color, *(pix - wrap)) && areNear(color, *(pix + wrap)) && + areNear(color, *(pix - wrap - 1)) && areNear(color, *(pix - wrap + 1)) && + areNear(color, *(pix + wrap - 1)) && areNear(color, *(pix + wrap + 1))) { + solidColors++; + if (!areNear(*pix, old) && !find(palette, *pix)) { + old = color; + count--; + palette.insert(color); + } + } + } + } + + raster->unlock(); + + if (solidColors < lx * ly / 2) { + palette.clear(); + buildPaletteForBlendedImages(palette, raster, maxColorCount); + } +} + +/*-- 全ての異なるピクセルの色を別のStyleにする --*/ +void TColorUtils::buildPrecisePalette(std::set &palette, const TRaster32P &raster, int maxColorCount) +{ + int lx = raster->getLx(); + int ly = raster->getLy(); + int wrap = raster->getWrap(); + + int x, y; + int count = maxColorCount; + raster->lock(); + for (y = 1; y < ly - 1 && count > 0; y++) { + TPixel *pix = raster->pixels(y); + for (x = 1; x < lx - 1 && count > 0; x++, pix++) { + if (!find(palette, *pix)) { + TPixel color = *pix; + count--; + palette.insert(color); + } + } + } + + raster->unlock(); + + /*-- 色数が最大値を超えたら、似ている色をまとめて1つのStyleにする手法を行う --*/ + if (count == 0) { + palette.clear(); + buildPalette(palette, raster, maxColorCount); + } +} + +//------------------------------------------------------------------------------ diff --git a/toonz/sources/common/tapptools/tenv.cpp b/toonz/sources/common/tapptools/tenv.cpp new file mode 100644 index 0000000..6441e23 --- /dev/null +++ b/toonz/sources/common/tapptools/tenv.cpp @@ -0,0 +1,720 @@ + + +#include "tenv.h" +#include "tsystem.h" +#include "tconvert.h" +#include "tfilepath_io.h" + +#include + +#ifdef LEVO_MACOSX + +#include "macofflinegl.h" +#include "tofflinegl.h" + +// Imposto l'offlineGL usando AGL (per togliere la dipendenza da X) + +TOfflineGL::Imp *MacOfflineGenerator1(const TDimension &dim) +{ + return new MacImplementation(dim); +} +#endif + +//#include + +//#include +//#include + +//using namespace std; + +#include +//#include +//#include + +using namespace TEnv; + +//========================================================= +// +// root dir +// +//========================================================= + +namespace +{ + +class EnvGlobals +{ // singleton + + string m_applicationName; + string m_applicationVersion; + string m_applicationFullName; + string m_moduleName; + string m_rootVarName; + string m_systemVarPrefix; + TFilePath m_registryRoot; + TFilePath m_envFile; + TFilePath *m_stuffDir; + TFilePath *m_dllRelativeDir; + + EnvGlobals() : m_stuffDir(0) {} + +public: + ~EnvGlobals() { delete m_stuffDir; } + + static EnvGlobals *instance() + { + static EnvGlobals _instance; + return &_instance; + } + + TFilePath getSystemVarPath(string varName) + { +#ifdef WIN32 + return m_registryRoot + varName; +#else + QString settingsPath = QString::fromStdString(getApplicationName()) + QString("_") + + QString::fromStdString(getApplicationVersion()) + QString(".app") + + QString("/Contents/Resources/SystemVar.ini"); + QSettings settings(settingsPath, QSettings::IniFormat); + QString qStr = QString::fromStdString(varName); + QString systemVar = settings.value(qStr).toString(); + //printf("getSystemVarPath: path:%s key:%s var:%s\n", settingsPath.toStdString().data(), varName.data(), systemVar.toStdString().data()); + return TFilePath(systemVar.toStdWString()); +#endif + } + + TFilePath getRootVarPath() + { + return getSystemVarPath(m_rootVarName); + } + + string getSystemVarValue(string varName) + { +#ifdef WIN32 + return TSystem::getSystemValue(getSystemVarPath(varName)).toStdString(); +#else + TFilePath systemVarPath = getSystemVarPath(varName); + if (systemVarPath.isEmpty()) { + std::cout << "varName:" << varName << " TOONZROOT not set..." << std::endl; + return ""; + } + return toString(systemVarPath); +/* + char *value = getenv(varName.c_str()); + if (!value) + { + std::cout << varName << " not set, returning TOONZROOT" << std::endl; + //value = getenv("TOONZROOT"); + value=""; + std::cout << "!!!value= "<< value << std::endl; + if (!value) + { + std::cout << varName << "TOONZROOT not set..." << std::endl; + //exit(-1); + return ""; + } + } + return string(value); + */ +#endif + } + + TFilePath getStuffDir() + { + if (m_stuffDir) + return *m_stuffDir; + return TFilePath(getSystemVarValue(m_rootVarName)); + } + void setStuffDir(const TFilePath &stuffDir) + { + delete m_stuffDir; + m_stuffDir = new TFilePath(stuffDir); + } + + void updateEnvFile() + { + TFilePath profilesDir = getSystemVarPathValue(getSystemVarPrefix() + "PROFILES"); + if (profilesDir == TFilePath()) + profilesDir = getStuffDir() + "profiles"; + m_envFile = profilesDir + "env" + (TSystem::getUserName().toStdString() + ".env"); + } + + void setApplication(string applicationName, string applicationVersion) + { + m_applicationName = applicationName; + m_applicationVersion = applicationVersion; + m_applicationFullName = m_applicationName + " " + m_applicationVersion; + m_moduleName = m_applicationName; + m_rootVarName = toUpper(m_applicationName) + "ROOT"; +#ifdef WIN32 + m_registryRoot = TFilePath("SOFTWARE\\OpenToonz\\") + m_applicationName + m_applicationVersion; +#endif + m_systemVarPrefix = m_applicationName; + updateEnvFile(); + } + + string getApplicationName() { return m_applicationName; } + string getApplicationVersion() { return m_applicationVersion; } + + TFilePath getEnvFile() { return m_envFile; } + + void setApplicationFullName(string applicationFullName) + { + m_applicationFullName = applicationFullName; + } + string getApplicationFullName() { return m_applicationFullName; } + + void setModuleName(string moduleName) { m_moduleName = moduleName; } + string getModuleName() { return m_moduleName; } + + void setRootVarName(string varName) + { + m_rootVarName = varName; + updateEnvFile(); + } + string getRootVarName() + { + return m_rootVarName; + } + + void setSystemVarPrefix(string prefix) + { + m_systemVarPrefix = prefix; + } + string getSystemVarPrefix() { return m_systemVarPrefix; } + + void setDllRelativeDir(const TFilePath &dllRelativeDir) + { + delete m_dllRelativeDir; + m_dllRelativeDir = new TFilePath(dllRelativeDir); + } + + TFilePath getDllRelativeDir() + { + if (m_dllRelativeDir) + return *m_dllRelativeDir; + return TFilePath("."); + } +}; + +/* +TFilePath EnvGlobals::getSystemPath(int id) +{ + std::map::iterator it = m_systemPaths.find(id); + if(it != m_systemPaths.end()) return it->second; + switch(id) + { + case StuffDir: return TFilePath(); + case ConfigDir: return getSystemPath(StuffDir) + "config"; + case ProfilesDir: return getSystemPath(StuffDir) + "profiles"; + default: return TFilePath(); + } +} + +void EnvGlobals::setSystemPath(int id, const TFilePath &fp) +{ + m_systemPaths[id] = fp; +} +*/ + +} // namespace + +//========================================================= +// +// Variable::Imp +// +//========================================================= + +class Variable::Imp +{ +public: + string m_name; + string m_value; + bool m_loaded, m_defaultDefined, m_assigned; + + Imp(string name) + : m_name(name), m_value(""), m_loaded(false), m_defaultDefined(false), m_assigned(false) {} +}; + +//========================================================= +// +// varaible manager (singleton) +// +//========================================================= + +namespace +{ + +class VariableSet +{ + + std::map m_variables; + bool m_loaded; + +public: + VariableSet() : m_loaded(false) {} + + ~VariableSet() + { + std::map::iterator it; + for (it = m_variables.begin(); it != m_variables.end(); ++it) + delete it->second; + } + + static VariableSet *instance() + { + static VariableSet instance; + return &instance; + } + + Variable::Imp *getImp(string name) + { + std::map::iterator it; + it = m_variables.find(name); + if (it == m_variables.end()) { + Variable::Imp *imp = new Variable::Imp(name); + m_variables[name] = imp; + return imp; + } else + return it->second; + } + + void commit() + { + //save(); + } + + void loadIfNeeded() + { + if (m_loaded) + return; + m_loaded = true; + try { + load(); + } catch (...) { + } + } + + void load(); + void save(); +}; + +//------------------------------------------------------------------- + +void VariableSet::load() +{ +#ifdef MACOSX + EnvGlobals::instance()->updateEnvFile(); +#endif + TFilePath fp = EnvGlobals::instance()->getEnvFile(); + if (fp == TFilePath()) + return; + Tifstream is(fp); + if (!is) + return; + char buffer[1024]; + while (is.getline(buffer, sizeof(buffer))) { + char *s = buffer; + while (*s == ' ') + s++; + char *t = s; + while ('a' <= *s && *s <= 'z' || 'A' <= *s && *s <= 'Z' || '0' <= *s && *s <= '9' || *s == '_') + s++; + string name(t, s - t); + if (name.size() == 0) + continue; + while (*s == ' ') + s++; + if (*s != '\"') + continue; + s++; + string value; + while (*s != '\n' && *s != '\0' && *s != '\"') { + if (*s != '\\') + value.push_back(*s); + else { + s++; + if (*s == '\\') + value.push_back('\\'); + else if (*s == '"') + value.push_back('"'); + else if (*s == 'n') + value.push_back('\n'); + else + continue; + } + s++; + } + Variable::Imp *imp = getImp(name); + imp->m_value = value; + imp->m_loaded = true; + } +} + +//------------------------------------------------------------------- + +void VariableSet::save() +{ + TFilePath fp = EnvGlobals::instance()->getEnvFile(); + if (fp == TFilePath()) + return; + bool exists = TFileStatus(fp.getParentDir()).doesExist(); + if (!exists) { + try { + TSystem::mkDir(fp.getParentDir()); + } catch (...) { + return; + } + } + Tofstream os(fp); + if (!os) + return; + std::map::iterator it; + for (it = m_variables.begin(); it != m_variables.end(); ++it) { + os << it->first << " \""; + string s = it->second->m_value; + for (int i = 0; i < (int)s.size(); i++) + if (s[i] == '\"') + os << "\\\""; + else if (s[i] == '\\') + os << "\\\\"; + else if (s[i] == '\n') + os << "\\n"; + else + os.put(s[i]); + os << "\"" << std::endl; + } +} + +//------------------------------------------------------------------- + +} // namespace + +//========================================================= + +Variable::Variable(string name) + : m_imp(VariableSet::instance()->getImp(name)) +{ +} + +//------------------------------------------------------------------- + +Variable::Variable(string name, string defaultValue) + : m_imp(VariableSet::instance()->getImp(name)) +{ + //assert(!m_imp->m_defaultDefined); + m_imp->m_defaultDefined = true; + if (!m_imp->m_loaded) + m_imp->m_value = defaultValue; +} + +//------------------------------------------------------------------- + +Variable::~Variable() +{ +} + +//------------------------------------------------------------------- + +string Variable::getName() const +{ + return m_imp->m_name; +} + +//------------------------------------------------------------------- + +string Variable::getValue() const +{ + VariableSet::instance()->loadIfNeeded(); + return m_imp->m_value; +} + +//------------------------------------------------------------------- + +void Variable::assignValue(string value) +{ + VariableSet *vs = VariableSet::instance(); + vs->loadIfNeeded(); + m_imp->m_value = value; + try { + vs->commit(); + } catch (...) { + } +} + +//=================================================================== + +void TEnv::setApplication(string applicationName, string applicationVersion) +{ + EnvGlobals::instance()->setApplication(applicationName, applicationVersion); + +#ifdef LEVO_MACOSX + TOfflineGL::defineImpGenerator(MacOfflineGenerator1); +#endif +} + +string TEnv::getApplicationName() +{ + return EnvGlobals::instance()->getApplicationName(); +} + +string TEnv::getApplicationVersion() +{ + return EnvGlobals::instance()->getApplicationVersion(); +} + +void TEnv::setApplicationFullName(string applicationFullName) +{ + EnvGlobals::instance()->setApplicationFullName(applicationFullName); +} + +string TEnv::getApplicationFullName() +{ + return EnvGlobals::instance()->getApplicationFullName(); +} + +void TEnv::setModuleName(string moduleName) +{ + EnvGlobals::instance()->setModuleName(moduleName); +} + +string TEnv::getModuleName() +{ + return EnvGlobals::instance()->getModuleName(); +} + +void TEnv::setRootVarName(string varName) +{ + EnvGlobals::instance()->setRootVarName(varName); +} + +string TEnv::getRootVarName() +{ + return EnvGlobals::instance()->getRootVarName(); +} + +TFilePath TEnv::getRootVarPath() +{ + return EnvGlobals::instance()->getRootVarPath(); +} + +string TEnv::getSystemVarStringValue(string varName) +{ + return EnvGlobals::instance()->getSystemVarValue(varName); +} + +TFilePath TEnv::getSystemVarPathValue(string varName) +{ + EnvGlobals *eg = EnvGlobals::instance(); + return TFilePath(eg->getSystemVarValue(varName)); +} + +TFilePathSet TEnv::getSystemVarPathSetValue(string varName) +{ + TFilePathSet lst; + string value = EnvGlobals::instance()->getSystemVarValue(varName); + int len = (int)value.size(); + int i = 0; + int j = value.find(';'); + while (j != string::npos) { + string s = value.substr(i, j - i); + lst.push_back(TFilePath(s)); + i = j + 1; + if (i >= len) + return lst; + j = value.find(';', i); + } + if (i < len) + lst.push_back(TFilePath(value.substr(i))); + return lst; +} + +void TEnv::setSystemVarPrefix(string varName) +{ + EnvGlobals::instance()->setSystemVarPrefix(varName); +} + +string TEnv::getSystemVarPrefix() +{ + return EnvGlobals::instance()->getSystemVarPrefix(); +} + +TFilePath TEnv::getStuffDir() +{ + //#ifdef MACOSX + //return TFilePath("/Applications/Toonz 5.0/Toonz 5.0 stuff"); + //#else + return EnvGlobals::instance()->getStuffDir(); + //#endif +} + +TFilePath TEnv::getConfigDir() +{ + TFilePath configDir = getSystemVarPathValue(getSystemVarPrefix() + "CONFIG"); + if (configDir == TFilePath()) + configDir = getStuffDir() + "config"; + return configDir; +} + +/*TFilePath TEnv::getProfilesDir() +{ + TFilePath fp(getStuffDir()); + return fp != TFilePath() ? fp + "profiles" : fp; +} +*/ +void TEnv::setStuffDir(const TFilePath &stuffDir) +{ + EnvGlobals::instance()->setStuffDir(stuffDir); +} + +TFilePath TEnv::getDllRelativeDir() +{ + return EnvGlobals::instance()->getDllRelativeDir(); +} + +void TEnv::setDllRelativeDir(const TFilePath &dllRelativeDir) +{ + EnvGlobals::instance()->setDllRelativeDir(dllRelativeDir); +} + +void TEnv::saveAllEnvVariables() +{ + VariableSet::instance()->save(); +} + +/* +void TEnv::defineSystemPath(SystemFileId id, const TFilePath ®istryName) +{ + string s = TSystem::getSystemValue(registryName); + if(s=="") return; + EnvGlobals::instance()->setSystemPath(id, TFilePath(s)); +} + +//--------------------------------------------------------- + + +TFilePath TEnv::getSystemPath(SystemFileId id) +{ + return EnvGlobals::instance()->getSystemPath(id); +} +*/ + +//========================================================= +// +// Variabili tipizzate +// +//========================================================= + +namespace +{ + +istream &operator>>(istream &is, TFilePath &path) +{ + string s; + is >> s; + //path = TFilePath(s); + return is; +} + +istream &operator>>(istream &is, TRect &rect) +{ + return is >> rect.x0 >> rect.y0 >> rect.x1 >> rect.y1; +} + +template +string toString2(T value) +{ + ostrstream ss; + ss << value << '\0'; + string s(ss.str()); + ss.freeze(false); + return s; +} + +template <> +string toString2(TRect value) +{ + ostrstream ss; + ss << value.x0 << " " << value.y0 << " " << value.x1 << " " << value.y1 << '\0'; + string s = ss.str(); + ss.freeze(false); + return s; +} + +template +void fromString(string s, T &value) +{ + if (s.empty()) + return; + istrstream is(s.c_str(), s.size()); + is >> value; +} + +void fromString(string s, string &value) +{ + value = s; +} + +} // namespace + +//------------------------------------------------------------------- + +IntVar::IntVar(string name, int defValue) : Variable(name, toString(defValue)) {} +IntVar::IntVar(string name) : Variable(name) {} +IntVar::operator int() const +{ + int v; + fromString(getValue(), v); + return v; +} +void IntVar::operator=(int v) { assignValue(toString(v)); } + +//------------------------------------------------------------------- + +DoubleVar::DoubleVar(string name, double defValue) : Variable(name, toString(defValue)) {} +DoubleVar::DoubleVar(string name) : Variable(name) {} +DoubleVar::operator double() const +{ + double v; + fromString(getValue(), v); + return v; +} +void DoubleVar::operator=(double v) { assignValue(toString(v)); } + +//------------------------------------------------------------------- + +StringVar::StringVar(string name, const string &defValue) : Variable(name, defValue) {} +StringVar::StringVar(string name) : Variable(name) {} +StringVar::operator string() const +{ + string v; + fromString(getValue(), v); + return v; +} +void StringVar::operator=(const string &v) { assignValue(v); } + +//------------------------------------------------------------------- + +FilePathVar::FilePathVar(string name, const TFilePath &defValue) : Variable(name, toString(defValue)) {} +FilePathVar::FilePathVar(string name) : Variable(name) {} +FilePathVar::operator TFilePath() const +{ + string v; + fromString(getValue(), v); + return TFilePath(v); +} +void FilePathVar::operator=(const TFilePath &v) { assignValue(toString(v)); } + +//------------------------------------------------------------------- + +RectVar::RectVar(string name, const TRect &defValue) : Variable(name, toString2(defValue)) {} +RectVar::RectVar(string name) : Variable(name) {} +RectVar::operator TRect() const +{ + TRect v; + fromString(getValue(), v); + return v; +} +void RectVar::operator=(const TRect &v) { assignValue(toString2(v)); } + +//========================================================= diff --git a/toonz/sources/common/tapptools/tparamundo.cpp b/toonz/sources/common/tapptools/tparamundo.cpp new file mode 100644 index 0000000..2825122 --- /dev/null +++ b/toonz/sources/common/tapptools/tparamundo.cpp @@ -0,0 +1,33 @@ + + +#include "tparamundo.h" +//#include "tparam.h" +#include "tundo.h" + +class ParamUndoManager : public TParamUndoManager +{ +public: + ParamUndoManager() {} + ~ParamUndoManager() {} + void onChange(const TParamChange &change); +}; + +//------------------------------------------------------------------- + +TParamUndoManager *TParamUndoManager::instance() +{ + static ParamUndoManager instance; + return &instance; +} + +//------------------------------------------------------------------- + +void ParamUndoManager::onChange(const TParamChange &change) +{ + assert(0); + + //if (!change.m_undoing && !change.m_dragging) + // TUndoManager::manager()->add(change.createUndo()); +} + +//------------------------------------------------------------------- diff --git a/toonz/sources/common/tapptools/ttimer.cpp b/toonz/sources/common/tapptools/ttimer.cpp new file mode 100644 index 0000000..8c7289c --- /dev/null +++ b/toonz/sources/common/tapptools/ttimer.cpp @@ -0,0 +1,356 @@ + + +#include "ttimer.h" +#include "texception.h" + +#ifdef WIN32 + +#include + +//moto strano: se togliamo l'include della glut non linka +#include + +//------------------------------------------------------------------------------ + +namespace +{ + +void CALLBACK ElapsedTimeCB(UINT uID, UINT uMsg, + DWORD dwUser, DWORD dw1, + DWORD dw2); +}; + +//------------------------------------------------------------------------------ + +class TTimer::Imp +{ +public: + Imp(string name, UINT timerRes, TTimer::Type type, TTimer *timer); + ~Imp(); + + void start(UINT delay) + { + if (m_started) + throw TException("The timer is already started"); + + m_timerID = timeSetEvent(delay, m_timerRes, + (LPTIMECALLBACK)ElapsedTimeCB, (DWORD) this, + m_type | TIME_CALLBACK_FUNCTION); + + m_delay = delay; + m_ticks = 0; + if (m_timerID == NULL) + throw TException("Unable to start timer"); + + m_started = true; + } + + void stop() + { + if (m_started) + timeKillEvent(m_timerID); + m_started = false; + } + + string getName() { return m_name; } + TUINT64 getTicks() { return m_ticks; } + UINT getDelay() { return m_delay; } + + string m_name; + + UINT m_timerRes; + UINT m_type; + TTimer *m_timer; + + UINT m_timerID; + UINT m_delay; + TUINT64 m_ticks; + bool m_started; + + TGenericTimerAction *m_action; +}; + +//------------------------------------------------------------------------------ + +TTimer::Imp::Imp(string name, UINT timerRes, TTimer::Type type, TTimer *timer) + : m_name(name), m_timerRes(timerRes), m_timer(timer), m_type(type), m_timerID(NULL), m_ticks(0), m_delay(0), m_started(false), m_action(0) +{ + + TIMECAPS tc; + + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + throw TException("Unable to create timer"); + } + + m_timerRes = tmin((int)tmax((int)tc.wPeriodMin, (int)m_timerRes), (int)tc.wPeriodMax); + timeBeginPeriod(m_timerRes); + + switch (type) { + case TTimer::OneShot: + m_type = TIME_ONESHOT; + break; + + case TTimer::Periodic: + m_type = TIME_PERIODIC; + break; + + default: + throw TException("Unexpected timer type"); + break; + } +} + +//------------------------------------------------------------------------------ + +TTimer::Imp::~Imp() +{ + stop(); + timeEndPeriod(m_timerRes); + + if (m_action) + delete m_action; +} + +//------------------------------------------------------------------------------ + +namespace +{ + +void CALLBACK ElapsedTimeCB(UINT uID, UINT uMsg, + DWORD dwUser, DWORD dw1, + DWORD dw2) +{ + TTimer::Imp *imp = reinterpret_cast(dwUser); + imp->m_ticks++; + if (imp->m_action) + imp->m_action->sendCommand(imp->m_ticks); +} +}; +#elif LINUX + +#include +#include +#include "tthread.h" +namespace +{ +Uint32 ElapsedTimeCB(Uint32 interval, void *param); +} + +class TTimer::Imp +{ +public: + Imp(string name, UINT timerRes, TTimer::Type type, TTimer *timer) + : m_action(0), m_ticks(0) + { + } + ~Imp() {} + + void start(UINT delay) + { + static bool first = true; + if (first) { + SDL_Init(SDL_INIT_TIMER); + first = false; + } + m_timerID = SDL_AddTimer(delay, ElapsedTimeCB, this); + } + + void stop() + { + SDL_RemoveTimer(m_timerID); + } + + string getName() { return m_name; } + TUINT64 getTicks() { return m_ticks; } + UINT getDelay() { return m_delay; } + + string m_name; + + UINT m_timerRes; + UINT m_type; + TTimer *m_timer; + + SDL_TimerID m_timerID; + UINT m_delay; + TUINT64 m_ticks; + bool m_started; + + TGenericTimerAction *m_action; +}; + +class SendCommandMSG : public TThread::Msg +{ + TTimer::Imp *m_ztimp; + +public: + SendCommandMSG(TTimer::Imp *ztimp) : TThread::Msg(), m_ztimp(ztimp) + { + } + ~SendCommandMSG() {} + TThread::Msg *clone() const { return new SendCommandMSG(*this); } + void onDeliver() + { + if (m_ztimp->m_action) + m_ztimp->m_action->sendCommand(m_ztimp->m_ticks); + } +}; + +namespace +{ +Uint32 ElapsedTimeCB(Uint32 interval, void *param) +{ + TTimer::Imp *imp = reinterpret_cast(param); + imp->m_ticks++; + SendCommandMSG(imp).send(); + return interval; +} +} + +#elif __sgi +class TTimer::Imp +{ +public: + Imp(string name, UINT timerRes, TTimer::Type type, TTimer *timer) + : m_action(0) {} + ~Imp() {} + + void start(UINT delay) + { + if (m_started) + throw TException("The timer is already started"); + + m_started = true; + } + + void stop() + { + m_started = false; + } + + string getName() { return m_name; } + TUINT64 getTicks() { return m_ticks; } + UINT getDelay() { return m_delay; } + + string m_name; + + UINT m_timerRes; + UINT m_type; + TTimer *m_timer; + + UINT m_timerID; + UINT m_delay; + TUINT64 m_ticks; + bool m_started; + + TGenericTimerAction *m_action; +}; +#elif MACOSX +class TTimer::Imp +{ +public: + Imp(string name, UINT timerRes, TTimer::Type type, TTimer *timer) + : m_action(0) {} + ~Imp() {} + + void start(UINT delay) + { + if (m_started) + throw TException("The timer is already started"); + throw TException("The timer is not yet available under MAC :("); + m_started = true; + } + + void stop() + { + m_started = false; + } + + string getName() { return m_name; } + TUINT64 getTicks() { return m_ticks; } + UINT getDelay() { return m_delay; } + + string m_name; + + UINT m_timerRes; + UINT m_type; + TTimer *m_timer; + + UINT m_timerID; + UINT m_delay; + TUINT64 m_ticks; + bool m_started; + + TGenericTimerAction *m_action; +}; + +#endif + +//=============================================================================== +// +// TTimer +// +//=============================================================================== + +TTimer::TTimer(const string &name, UINT timerRes, Type type) +{ + m_imp = new TTimer::Imp(name, timerRes, type, this); +} + +//-------------------------------------------------------------------------------- + +TTimer::~TTimer() +{ + delete m_imp; +} + +//-------------------------------------------------------------------------------- + +void TTimer::start(UINT delay) +{ + m_imp->start(delay); +} + +//-------------------------------------------------------------------------------- + +bool TTimer::isStarted() const +{ + return m_imp->m_started; +} + +//-------------------------------------------------------------------------------- + +void TTimer::stop() +{ + m_imp->stop(); +} + +//-------------------------------------------------------------------------------- + +string TTimer::getName() const +{ + return m_imp->getName(); +} + +//-------------------------------------------------------------------------------- + +TUINT64 TTimer::getTicks() const +{ + return m_imp->getTicks(); +} + +//-------------------------------------------------------------------------------- + +UINT TTimer::getDelay() const +{ + return m_imp->getDelay(); +} + +//-------------------------------------------------------------------------------- + +void TTimer::setAction(TGenericTimerAction *action) +{ + if (m_imp->m_action) + delete m_imp->m_action; + + m_imp->m_action = action; +} diff --git a/toonz/sources/common/tcache/timagecache.cpp b/toonz/sources/common/tcache/timagecache.cpp new file mode 100644 index 0000000..3bed35c --- /dev/null +++ b/toonz/sources/common/tcache/timagecache.cpp @@ -0,0 +1,2048 @@ + + +#ifndef _DEBUG +#undef _STLP_DEBUG +#else +#define _STLP_DEBUG 1 + +#endif + +#ifdef TNZCORE_LIGHT +#ifdef _DEBUGTOONZ +#undef _DEBUGTOONZ +#endif +#else +#ifdef _DEBUG +#define _DEBUGTOONZ _DEBUG +#endif +#endif + +#include "timagecache.h" +#include "trasterimage.h" +#ifndef TNZCORE_LIGHT +#include "tvectorimage.h" +#include "trastercm.h" +#include "tropcm.h" +#endif + +#include "tcodec.h" +#include "tfilepath_io.h" +#include "tconvert.h" +#include "tsystem.h" + +#include "traster.h" + +//#include "tstopwatch.h" +#include "tconvert.h" +#include "tbigmemorymanager.h" + +#include "tstream.h" +#include "tenv.h" +#include +#include +#include +#ifdef WIN32 +#include +#endif + +// Qt includes +#include + +//------------------------------------------------------------------------------ + +#undef DVAPI +#undef DVVAR +#ifdef TSYSTEM_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class ImageBuilder; +class ImageInfo; + +//std::ofstream os("C:\\cache.txt"); + +TUINT32 HistoryCount = 0; +//------------------------------------------------------------------------------ + +class TheCodec : public TRasterCodecLz4 +{ +public: + static TheCodec *instance() + { + if (!_instance) + _instance = new TheCodec(); + + return _instance; + } + + void reset() + { + if (_instance) + _instance->TRasterCodecLz4::reset(); + } + +private: + static TheCodec *_instance; + TheCodec() : TRasterCodecLz4("Lz4_Codec", false) {} +}; + +TheCodec *TheCodec::_instance = 0; + +//------------------------------------------------------------------------------ + +class CacheItem : public TSmartObject +{ + DECLARE_CLASS_CODE +public: + CacheItem() + : m_cantCompress(false), m_builder(0), m_imageInfo(0), m_modified(false) {} + + CacheItem(ImageBuilder *builder, ImageInfo *imageInfo) + : m_cantCompress(false), m_builder(builder), m_imageInfo(imageInfo), m_historyCount(0), m_modified(false) + { + } + + virtual ~CacheItem() {} + + virtual TUINT32 getSize() const = 0; + + // getImage restituisce un'immagine non compressa + virtual TImageP getImage() const = 0; + + bool m_cantCompress; + ImageBuilder *m_builder; + ImageInfo *m_imageInfo; + string m_id; + TUINT32 m_historyCount; + bool m_modified; +}; + +#ifdef WIN32 +template class DVAPI TSmartPointerT; +#endif +typedef TSmartPointerT CacheItemP; + +DEFINE_CLASS_CODE(CacheItem, 101) + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class ImageInfo +{ + +public: + TDimension m_size; + ImageInfo(const TDimension &size) : m_size(size) {} + virtual ~ImageInfo() {} + virtual ImageInfo *clone() = 0; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class ImageBuilder +{ +public: + virtual ~ImageBuilder() {} + virtual ImageBuilder *clone() = 0; + virtual TImageP build(ImageInfo *info, const TRasterP &ras) = 0; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class RasterImageInfo : public ImageInfo +{ +public: + RasterImageInfo(const TRasterImageP &ri); + + void setInfo(const TRasterImageP &ri); + + ImageInfo *clone(); + + double m_dpix, m_dpiy; + string m_name; + TRect m_savebox; + bool m_isOpaque; + TPoint m_offset; + int m_subs; +}; + +RasterImageInfo::RasterImageInfo(const TRasterImageP &ri) + : ImageInfo(ri->getRaster()->getSize()) +{ + ri->getDpi(m_dpix, m_dpiy); + m_name = ri->getName(); + m_savebox = ri->getSavebox(); + m_isOpaque = ri->isOpaque(); + m_offset = ri->getOffset(); + m_subs = ri->getSubsampling(); +} + +void RasterImageInfo::setInfo(const TRasterImageP &ri) +{ + ri->setDpi(m_dpix, m_dpiy); + ri->setName(m_name); + ri->setSavebox(m_savebox); + ri->setOpaqueFlag(m_isOpaque); + ri->setOffset(m_offset); + ri->setSubsampling(m_subs); +} + +ImageInfo *RasterImageInfo::clone() +{ + return new RasterImageInfo(*this); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +#ifndef TNZCORE_LIGHT + +#include "tpalette.h" +#include "ttoonzimage.h" + +class ToonzImageInfo : public ImageInfo +{ +public: + ToonzImageInfo(const TToonzImageP &ti); + ~ToonzImageInfo() + { + if (m_palette) + m_palette->release(); + } + + ImageInfo *clone() + { + ToonzImageInfo *ret = new ToonzImageInfo(*this); + if (ret->m_palette) + ret->m_palette->addRef(); + return ret; + } + + void setInfo(const TToonzImageP &ti); + + double m_dpix, m_dpiy; + string m_name; + TRect m_savebox; + TPoint m_offset; + int m_subs; + TPalette *m_palette; +}; + +ToonzImageInfo::ToonzImageInfo(const TToonzImageP &ti) : ImageInfo(ti->getSize()) +{ + m_palette = ti->getPalette(); + if (m_palette) + m_palette->addRef(); + + ti->getDpi(m_dpix, m_dpiy); + m_savebox = ti->getSavebox(); + m_offset = ti->getOffset(); + m_subs = ti->getSubsampling(); +} + +void ToonzImageInfo::setInfo(const TToonzImageP &ti) +{ + ti->setPalette(m_palette); + ti->setDpi(m_dpix, m_dpiy); + ti->setOffset(m_offset); + ti->setSubsampling(m_subs); +} + +#endif + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class RasterImageBuilder : public ImageBuilder +{ +public: + ImageBuilder *clone() + { + return new RasterImageBuilder(*this); + } + + TImageP build(ImageInfo *info, const TRasterP &ras); +}; + +TImageP RasterImageBuilder::build(ImageInfo *info, const TRasterP &ras) +{ + RasterImageInfo *riInfo = dynamic_cast(info); + assert(riInfo); + + int rcount = ras->getRefCount(); + TRasterImageP ri = new TRasterImage(); +#ifdef _DEBUGTOONZ + ras->m_cashed = true; +#endif + ri->setRaster(ras); + riInfo->setInfo(ri); + assert(ras->getRefCount() > rcount); + return ri; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +#ifndef TNZCORE_LIGHT + +class ToonzImageBuilder : public ImageBuilder +{ +public: + ImageBuilder *clone() + { + return new ToonzImageBuilder(*this); + } + + TImageP build(ImageInfo *info, const TRasterP &ras); +}; + +TImageP ToonzImageBuilder::build(ImageInfo *info, const TRasterP &ras) +{ + ToonzImageInfo *tiInfo = dynamic_cast(info); + assert(tiInfo); + + TRasterCM32P rasCM32 = ras; + assert(rasCM32); + + TRasterCM32P imgRasCM32; + + assert(TRect(tiInfo->m_size).contains(tiInfo->m_savebox)); + + if (ras->getSize() != tiInfo->m_size) { + TRasterCM32P fullRas(tiInfo->m_size); + TRect rectToExtract(tiInfo->m_savebox); + TPixelCM32 bgColor; + fullRas->fillOutside(tiInfo->m_savebox, bgColor); + fullRas->extractT(rectToExtract)->copy(ras); + assert(rectToExtract == tiInfo->m_savebox); + imgRasCM32 = fullRas; + } else + imgRasCM32 = rasCM32; +#ifdef _DEBUG + imgRasCM32->m_cashed = true; +#endif + TToonzImageP ti = new TToonzImage(imgRasCM32, tiInfo->m_savebox); + tiInfo->setInfo(ti); + return ti; +} +#endif + +//------------------------------------------------------------------------------ + +class UncompressedOnMemoryCacheItem : public CacheItem +{ +public: + UncompressedOnMemoryCacheItem(const TImageP &image) : m_image(image) + { + TRasterImageP ri = m_image; + + if (ri) + m_imageInfo = new RasterImageInfo(ri); +#ifndef TNZCORE_LIGHT + else { + TToonzImageP ti = m_image; + if (ti) + m_imageInfo = new ToonzImageInfo(ti); + else + m_imageInfo = 0; + } +#else + else + m_imageInfo = 0; +#endif + } + + ~UncompressedOnMemoryCacheItem() + { + if (m_imageInfo) + delete m_imageInfo; + } + + TUINT32 getSize() const; + TImageP getImage() const { return m_image; } + + TImageP m_image; +}; + +#ifdef WIN32 +template class DVAPI TSmartPointerT; +template class DVAPI TDerivedSmartPointerT; +#endif +typedef TDerivedSmartPointerT UncompressedOnMemoryCacheItemP; + +//------------------------------------------------------------------------------ + +TUINT32 UncompressedOnMemoryCacheItem::getSize() const +{ + TRasterImageP ri = m_image; + if (ri) { + TRasterP ras = ri->getRaster(); + if (ras) + return ras->getLy() * ras->getRowSize(); + else + return 0; + } else { +#ifndef TNZCORE_LIGHT + TToonzImageP ti = m_image; + if (ti) { + TDimension size = ti->getSize(); + return size.lx * size.ly * sizeof(TPixelCM32); + } +#endif + } + + return 0; +} + +//------------------------------------------------------------------------------ + +class CompressedOnMemoryCacheItem : public CacheItem +{ +public: + CompressedOnMemoryCacheItem(const TImageP &img); + + CompressedOnMemoryCacheItem(const TRasterP &compressedRas, + ImageBuilder *builder, + ImageInfo *info); + + ~CompressedOnMemoryCacheItem(); + + TUINT32 getSize() const; + TImageP getImage() const; + + TRasterP m_compressedRas; +}; + +#ifdef WIN32 +template class DVAPI TSmartPointerT; +template class DVAPI TDerivedSmartPointerT; +#endif +typedef TDerivedSmartPointerT CompressedOnMemoryCacheItemP; + +//------------------------------------------------------------------------------ + +CompressedOnMemoryCacheItem::CompressedOnMemoryCacheItem(const TImageP &img) + : m_compressedRas() +{ + TRasterImageP ri = img; + if (ri) { + m_imageInfo = new RasterImageInfo(ri); + m_builder = new RasterImageBuilder(); + TINT32 buffSize = 0; + m_compressedRas = TheCodec::instance()->compress(ri->getRaster(), 1, buffSize); + } +#ifndef TNZCORE_LIGHT + else { + TToonzImageP ti = img; + if (ti) { + m_imageInfo = new ToonzImageInfo(ti); + m_builder = new ToonzImageBuilder(); + TRasterCM32P rasCM32 = ti->getRaster(); + TINT32 buffSize = 0; + m_compressedRas = TheCodec::instance()->compress(rasCM32, 1, buffSize); + } else + assert(false); + } +#else + else + assert(false); +#endif +} + +//------------------------------------------------------------------------------ + +CompressedOnMemoryCacheItem::CompressedOnMemoryCacheItem(const TRasterP &ras, + ImageBuilder *builder, + ImageInfo *info) + : CacheItem(builder, info), m_compressedRas(ras) +{ +} + +//------------------------------------------------------------------------------ + +CompressedOnMemoryCacheItem::~CompressedOnMemoryCacheItem() +{ + delete m_imageInfo; +} + +//------------------------------------------------------------------------------ + +TUINT32 CompressedOnMemoryCacheItem::getSize() const +{ + if (m_compressedRas) + return m_compressedRas->getLx(); + else + return 0; +} + +//------------------------------------------------------------------------------ + +TImageP CompressedOnMemoryCacheItem::getImage() const +{ + assert(m_compressedRas); + + // PER IL MOMENTO DISCRIMINO: DA ELIMINARE + TRasterP ras; + + TheCodec::instance()->decompress(m_compressedRas, ras); +#ifdef _DEBUGTOONZ + ras->m_cashed = true; +#endif +#ifndef TNZCORE_LIGHT + ToonzImageBuilder *tibuilder = dynamic_cast(m_builder); + if (tibuilder) + return tibuilder->build(m_imageInfo, ras); + else +#endif + return m_builder->build(m_imageInfo, ras); +} + +//------------------------------------------------------------------------------ + +class CompressedOnDiskCacheItem : public CacheItem +{ +public: + CompressedOnDiskCacheItem(const TFilePath &fp, + const TRasterP &compressedRas, + ImageBuilder *builder, + ImageInfo *info); + + ~CompressedOnDiskCacheItem(); + + TUINT32 getSize() const { return 0; } + TImageP getImage() const; + TFilePath m_fp; +}; + +#ifdef WIN32 +template class DVAPI TSmartPointerT; +template class DVAPI TDerivedSmartPointerT; +#endif +typedef TDerivedSmartPointerT CompressedOnDiskCacheItemP; + +//------------------------------------------------------------------------------ + +CompressedOnDiskCacheItem::CompressedOnDiskCacheItem(const TFilePath &fp, + const TRasterP &compressedRas, + ImageBuilder *builder, + ImageInfo *info) + : CacheItem(builder, info), m_fp(fp) +{ + compressedRas->lock(); + + Tofstream oss(m_fp); + assert(compressedRas->getLy() == 1 && compressedRas->getPixelSize() == 1); + TUINT32 size = compressedRas->getLx(); + oss.write((char *)&size, sizeof(TUINT32)); + oss.write((char *)compressedRas->getRawData(), size); + assert(!oss.fail()); + + compressedRas->unlock(); +} + +//------------------------------------------------------------------------------ + +CompressedOnDiskCacheItem::~CompressedOnDiskCacheItem() +{ + delete m_imageInfo; + TSystem::deleteFile(m_fp); +} + +//------------------------------------------------------------------------------ + +TImageP CompressedOnDiskCacheItem::getImage() const +{ + Tifstream is(m_fp); + TUINT32 dataSize; + is.read((char *)&dataSize, sizeof(TUINT32)); + TRasterGR8P ras(dataSize, 1); + ras->lock(); + UCHAR *data = ras->getRawData(); + is.read((char *)data, dataSize); + assert(!is.fail()); + ras->unlock(); + CompressedOnMemoryCacheItem item(ras, m_builder->clone(), m_imageInfo->clone()); + return item.getImage(); +} + +//------------------------------------------------------------------------------ + +class UncompressedOnDiskCacheItem : public CacheItem +{ + int m_pixelsize; + +public: + UncompressedOnDiskCacheItem(const TFilePath &fp, + const TImageP &img); + + ~UncompressedOnDiskCacheItem(); + + TUINT32 getSize() const { return 0; } + TImageP getImage() const; + //TRaster32P getRaster32() const; + + TFilePath m_fp; +}; +#ifdef WIN32 +template class DVAPI TSmartPointerT; +template class DVAPI TDerivedSmartPointerT; +#endif +typedef TDerivedSmartPointerT UncompressedOnDiskCacheItemP; + +//------------------------------------------------------------------------------ + +UncompressedOnDiskCacheItem::UncompressedOnDiskCacheItem(const TFilePath &fp, + const TImageP &image) + : CacheItem(0, 0), m_fp(fp) +{ + TRasterImageP ri = image; + + TRasterP ras; + if (ri) { + m_imageInfo = new RasterImageInfo(ri); + ras = ri->getRaster(); + } +#ifndef TNZCORE_LIGHT + else { + TToonzImageP ti = image; + if (ti) { + m_imageInfo = new ToonzImageInfo(ti); + ras = ti->getRaster(); + } else + assert(false); + } +#else + else + assert(false); +#endif + + m_builder = 0; + + int dataSize = ras->getLx() * ras->getLy() * ras->getPixelSize(); + + int lx = ras->getLx(); + int ly = ras->getLy(); + int wrap = ras->getWrap(); + m_pixelsize = ras->getPixelSize(); + + Tofstream oss(m_fp); + //oss.write((char*)&dataSize, sizeof(TUINT32)); + //assert(!oss.fail()); + ras->lock(); + if (lx == wrap) { + oss.write((char *)ras->getRawData(), dataSize); + assert(!oss.fail()); + } else { + char *buf = (char *)ras->getRawData(); + for (int i = 0; i < ly; i++, buf += wrap) { + oss.write(buf, lx * m_pixelsize); + assert(!oss.fail()); + } + } + ras->unlock(); +} + +//------------------------------------------------------------------------------ + +UncompressedOnDiskCacheItem::~UncompressedOnDiskCacheItem() +{ + delete m_imageInfo; + TSystem::deleteFile(m_fp); +} + +//------------------------------------------------------------------------------ + +TImageP UncompressedOnDiskCacheItem::getImage() const +{ + Tifstream is(m_fp); + TUINT32 dataSize = m_imageInfo->m_size.lx * m_imageInfo->m_size.ly * m_pixelsize; + + //is.read((char*)&dataSize, sizeof(TUINT32)); + //assert(unsigned(m_lx*m_ly*m_pixelsize)==dataSize); + + TRasterP ras; + + RasterImageInfo *rii = dynamic_cast(m_imageInfo); + + if (rii) { + if (m_pixelsize == 4) + ras = (TRasterP)(TRaster32P(rii->m_size)); + else if (m_pixelsize == 8) + ras = (TRasterP)(TRaster64P(rii->m_size)); + else if (m_pixelsize == 1) + ras = (TRasterP)(TRasterGR8P(rii->m_size)); + else if (m_pixelsize == 2) + ras = (TRasterP)(TRasterGR16P(rii->m_size)); + else + assert(false); + ras->lock(); + char *data = (char *)ras->getRawData(); + is.read(data, dataSize); + ras->unlock(); +#ifdef _DEBUGTOONZ + ras->m_cashed = true; +#endif + + return RasterImageBuilder().build(m_imageInfo, ras); + } +#ifndef TNZCORE_LIGHT + else { + ToonzImageInfo *tii = dynamic_cast(m_imageInfo); + if (tii) { + ras = (TRasterP)(TRasterCM32P(tii->m_size)); + ras->lock(); + char *data = (char *)ras->getRawData(); + is.read(data, dataSize); + ras->unlock(); +#ifdef _DEBUG + ras->m_cashed = true; +#endif + + return ToonzImageBuilder().build(m_imageInfo, ras); + } else { + assert(false); + return 0; + } + } +#else + else { + assert(false); + return 0; + } +#endif +} + +//------------------------------------------------------------------------------ + +string TImageCache::getUniqueId(void) +{ + static TAtomicVar count; + std::stringstream ss; + ss << ++count; + return "IMAGECACHEUNIQUEID" + ss.str(); +} + +class TImageCache::Imp +{ +public: + Imp() : m_rootDir() + { + //ATTENZIONE: e' molto piu' veloce se si usa memoria fisica + //invece che virtuale: la virtuale e' tanta, non c'e' quindi bisogno + //di comprimere le immagini, che grandi come sono vengono swappate su disco + if (TBigMemoryManager::instance()->isActive()) + return; + + m_reservedMemory = (TINT64)(TSystem::getMemorySize(true) * 0.10); + if (m_reservedMemory < 64 * 1024) + m_reservedMemory = 64 * 1024; + } + + ~Imp() + { + if (m_rootDir != TFilePath()) + TSystem::rmDirTree(m_rootDir); + } + + bool inline notEnoughMemory() + { + if (TBigMemoryManager::instance()->isActive()) + return TBigMemoryManager::instance()->getAvailableMemoryinKb() < 50 * 1024; + else + return TSystem::memoryShortage(); + } + + void doCompress(); + void doCompress(string id); + UCHAR *compressAndMalloc(TUINT32 requestedSize); // compress in the cache till it can nallocate the requested memory + void outputMap(UINT chunkRequested, string filename); + void remove(const string &id); + void remap(const string &dstId, const string &srcId); + TImageP get(const string &id, bool toBeModified); + void add(const string &id, const TImageP &img, bool overwrite); + TFilePath m_rootDir; + +#ifndef TNZCORE_LIGHT + QThreadStorage m_isEnabled; +#else + bool m_isEnabled; +#endif + + map m_uncompressedItems; + map m_itemHistory; + map m_compressedItems; + map m_itemsByImagePointer; //items ordered by ImageP.getPointer() + map m_duplicatedItems; //for duplicated items (when id1!=id2 but image1==image2) in the map: key is dup id, value is main id + //memoria fisica totale della macchina che non puo' essere utilizzata; + TINT64 m_reservedMemory; + TThread::Mutex m_mutex; + + static int m_fileid; +}; + +int TImageCache::Imp::m_fileid; + +//------------------------------------------------------------------------------ +namespace +{ +inline void *getPointer(const TImageP &img) +{ + TRasterImageP rimg = img; + if (rimg) + return rimg->getRaster().getPointer(); +#ifndef TNZCORE_LIGHT + TToonzImageP timg = img; + if (timg) + return timg->getRaster().getPointer(); +#endif + + return img.getPointer(); +} + +// Returns true or false whether the image or its eventual raster are +// referenced by someone other than Toonz cache. +inline TINT32 hasExternalReferences(const TImageP &img) +{ + int refCount; + + { + TRasterImageP rimg = img; + if (rimg) + refCount = rimg->getRaster()->getRefCount(); + } + +#ifndef TNZCORE_LIGHT + { + TToonzImageP timg = img; + if (timg) + refCount = timg->getRaster()->getRefCount() - 1; //!!! the TToonzImage::getRaster method increments raster refCount!(the TRasterImage::getRaster don't) + } +#endif + + return tmax(refCount, img->getRefCount()) > 1; +} +} +//------------------------------------------------------------------------------ + +void TImageCache::Imp::doCompress() +{ + // se la memoria usata per mantenere le immagini decompresse e' superiore + // a un dato valore, comprimo alcune immagini non compresse non checked-out + // in modo da liberare memoria + + // per il momento scorre tutte le immagini alla ricerca di immagini + // non compresse non checked-out + + TThread::MutexLocker sl(&m_mutex); + + std::map::iterator itu = m_itemHistory.begin(); + + for (; itu != m_itemHistory.end() && notEnoughMemory();) { + std::map::iterator it = m_uncompressedItems.find(itu->second); + assert(it != m_uncompressedItems.end()); + CacheItemP item = it->second; + + UncompressedOnMemoryCacheItemP uitem = item; + if (item->m_cantCompress || + (uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image)))) { + ++itu; + continue; + } + string id = it->first; + +#ifdef WIN32 + assert(itu->first == it->second->m_historyCount); + itu = m_itemHistory.erase(itu); + m_itemsByImagePointer.erase(getPointer(item->getImage())); + m_uncompressedItems.erase(it); +#else + std::map::iterator itu2 = itu; + itu++; + m_itemHistory.erase(itu2); + m_itemsByImagePointer.erase(item->getImage().getPointer()); + m_uncompressedItems.erase(it); +#endif + + if (m_compressedItems.find(id) == m_compressedItems.end()) { + assert(uitem); + item->m_cantCompress = true; + CacheItemP newItem = new CompressedOnMemoryCacheItem(item->getImage()); //WARNING the codec buffer allocation can CHANGE the cache. + item->m_cantCompress = false; + if (newItem->getSize() == 0) ///non c'era memoria sufficiente per il buffer compresso.... + { + assert(m_rootDir != TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + newItem = new UncompressedOnDiskCacheItem(fp, item->getImage()); + } + m_compressedItems[id] = newItem; + item = CacheItemP(); + uitem = UncompressedOnMemoryCacheItemP(); + //doCompress();//restart, since interators can have been changed (see comment above) + //return; + itu = m_itemHistory.begin(); + } + } + + // se il quantitativo di memoria utilizzata e' superiore a un dato valore, sposto + // su disco alcune immagini compresse in modo da liberare memoria + + if (itu != m_itemHistory.end()) //memory is enough! + return; + + std::map::iterator itc = m_compressedItems.begin(); + for (; itc != m_compressedItems.end() && notEnoughMemory(); ++itc) { + CacheItemP item = itc->second; + if (item->m_cantCompress) + continue; + + CompressedOnMemoryCacheItemP citem = itc->second; + if (citem) { + assert(m_rootDir != TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + + CacheItemP newItem = new CompressedOnDiskCacheItem(fp, citem->m_compressedRas, + citem->m_builder->clone(), citem->m_imageInfo->clone()); + + itc->second = 0; + m_compressedItems[itc->first] = newItem; + } + } +} + +//------------------------------------------------------------------------------ + +void TImageCache::Imp::doCompress(string id) +{ + TThread::MutexLocker sl(&m_mutex); + + // search id in m_uncompressedItems + std::map::iterator it = m_uncompressedItems.find(id); + if (it == m_uncompressedItems.end()) + return; // id not found: return + + // is item suitable for compression ? + CacheItemP item = it->second; + UncompressedOnMemoryCacheItemP uitem = item; + if (item->m_cantCompress || + (uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image)))) + return; + + // search id in m_itemHistory + std::map::iterator itu = m_itemHistory.begin(); + while (itu != m_itemHistory.end() && itu->second != id) + ++itu; + if (itu == m_itemHistory.end()) + return; // id not found: return + +// delete itu from m_itemHistory +#ifdef WIN32 + assert(itu->first == it->second->m_historyCount); + itu = m_itemHistory.erase(itu); + m_itemsByImagePointer.erase(getPointer(item->getImage())); +#else + std::map::iterator itu2 = itu; + itu++; + m_itemHistory.erase(itu2); + m_itemsByImagePointer.erase(item->getImage().getPointer()); +#endif + + // delete item from m_uncompressedItems + m_uncompressedItems.erase(it); + + // check if item has been already compressed. this should never happen + if (m_compressedItems.find(id) != m_compressedItems.end()) + return; + + assert(uitem); + item->m_cantCompress = true; // ?? + CacheItemP newItem = new CompressedOnMemoryCacheItem(item->getImage()); //WARNING the codec buffer allocation can CHANGE the cache. + item->m_cantCompress = false; // ?? + if (newItem->getSize() == 0) ///non c'era memoria sufficiente per il buffer compresso.... + { + assert(m_rootDir != TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + newItem = new UncompressedOnDiskCacheItem(fp, item->getImage()); + } + m_compressedItems[id] = newItem; + item = CacheItemP(); + uitem = UncompressedOnMemoryCacheItemP(); +} + +/* + // se il quantitativo di memoria utilizzata e' superiore a un dato valore, sposto + // su disco alcune immagini compresse in modo da liberare memoria + + if (itu != m_itemHistory.end()) //memory is enough! + return; + + std::map::iterator itc = m_compressedItems.begin(); + for ( ; itc != m_compressedItems.end() && notEnoughMemory(); ++itc) + { + CacheItemP item = itc->second; + if (item->m_cantCompress) + continue; + + CompressedOnMemoryCacheItemP citem = itc->second; + if (citem) + { + assert(m_rootDir!=TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + + CacheItemP newItem = new CompressedOnDiskCacheItem(fp, citem->m_compressedRas, + citem->m_builder->clone(), citem->m_imageInfo->clone()); + + itc->second = 0; + m_compressedItems[itc->first] = newItem; + } + } + */ + +//------------------------------------------------------------------------------ + +UCHAR *TImageCache::Imp::compressAndMalloc(TUINT32 size) +{ + UCHAR *buf = 0; + + TThread::MutexLocker sl(&m_mutex); + + TheCodec::instance()->reset(); + + //if (size!=0) + // size = size>>10; + + //assert(size==0 || TBigMemoryManager::instance()->isActive()); + + std::map::iterator itu = m_itemHistory.begin(); + while ((buf = TBigMemoryManager::instance()->getBuffer(size)) == 0 && + itu != m_itemHistory.end()) //>TBigMemoryManager::instance()->getAvailableMemoryinKb())) + { + std::map::iterator it = m_uncompressedItems.find(itu->second); + assert(it != m_uncompressedItems.end()); + CacheItemP item = it->second; + + UncompressedOnMemoryCacheItemP uitem = item; + if (item->m_cantCompress || (uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image)))) { + ++itu; + continue; + } + + if (m_compressedItems.find(it->first) == m_compressedItems.end()) { + assert(uitem); + CacheItemP newItem; + //newItem = new CompressedOnMemoryCacheItem(item->getImage()); + //if (newItem->getSize()==0) + // { + assert(m_rootDir != TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + newItem = new UncompressedOnDiskCacheItem(fp, item->getImage()); + // } + + m_compressedItems[it->first] = newItem; + } + +#ifdef WIN32 + assert(itu->first == it->second->m_historyCount); + itu = m_itemHistory.erase(itu); + m_itemsByImagePointer.erase(getPointer(item->getImage())); + m_uncompressedItems.erase(it); +#else + std::map::iterator itu2 = itu; + itu++; + m_itemHistory.erase(itu2); + m_itemsByImagePointer.erase(item->getImage().getPointer()); + m_uncompressedItems.erase(it); +#endif + } + + if (buf != 0) + return buf; + + std::map::iterator itc = m_compressedItems.begin(); + for (; itc != m_compressedItems.end() && + (buf = TBigMemoryManager::instance()->getBuffer(size)) == 0; + ++itc) { + CacheItemP item = itc->second; + if (item->m_cantCompress) + continue; + + CompressedOnMemoryCacheItemP citem = itc->second; + if (citem) { + assert(m_rootDir != TFilePath()); + TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++)); + + CacheItemP newItem = new CompressedOnDiskCacheItem( + fp, citem->m_compressedRas, + citem->m_builder->clone(), citem->m_imageInfo->clone()); + + itc->second = 0; + m_compressedItems[itc->first] = newItem; + } + } + + return buf; +} + +//------------------------------------------------------------------------------ + +namespace +{ + +int check = 0; +const int magic = 123456; +} + +static TImageCache *CacheInstance = 0; + +TImageCache *TImageCache::instance() +{ + if (CacheInstance == 0) + CacheInstance = new TImageCache(); + return CacheInstance; + /* + if (!ImageCache::m_instance) + { + ImageCache::m_instance = new ImageCache; + ImageCache::m_destroyer.m_imageCache = ImageCache::m_instance; + } + return ImageCache::m_instance; + */ +} + +//------------------------------------------------------------------------------ + +TImageCache::TImageCache() + : m_imp(new Imp()) +{ + assert(check == 0); + check = magic; +} + +//------------------------------------------------------------------------------ + +TImageCache::~TImageCache() +{ + assert(check == magic); + check = -1; + delete m_imp; + CacheInstance = 0; +} + +//------------------------------------------------------------------------------ + +void TImageCache::setEnabled(bool isEnabled) +{ +#ifndef TNZCORE_LIGHT + QThreadStorage &storage = m_imp->m_isEnabled; + + if (storage.hasLocalData() && *(storage.localData()) == isEnabled) + return; + if (!storage.hasLocalData()) + storage.setLocalData(new bool(isEnabled)); + else + *(storage.localData()) = isEnabled; +#else + m_imp->m_isEnabled = isEnabled; +#endif +} + +//------------------------------------------------------------------------------ + +bool TImageCache::isEnabled() +{ +#ifndef TNZCORE_LIGHT + QThreadStorage &storage = m_imp->m_isEnabled; + + if (!storage.hasLocalData()) + return true; + return *(storage.localData()); +#else + return m_isEnabled; +#endif +} + +//------------------------------------------------------------------------------ + +void TImageCache::setRootDir(const TFilePath &cacheDir) +{ + if (m_imp->m_rootDir != TFilePath()) + return; + + m_imp->m_rootDir = cacheDir + TFilePath(toString(TSystem::getProcessId())); + +#ifndef TNZCORE_LIGHT + TFileStatus fs1(m_imp->m_rootDir); + + if (!fs1.doesExist()) + TSystem::mkDir(m_imp->m_rootDir); + +#endif +} + +//------------------------------------------------------------------------------ +/* +TFilePath TImageCache::getRootDir() const +{ +return m_imp->m_rootDir; +} +*/ +//------------------------------------------------------------------------------ + +UCHAR *TImageCache::compressAndMalloc(TUINT32 requestedSize) +{ + return m_imp->compressAndMalloc(requestedSize); +} + +//------------------------------------------------------------------------------ + +void TImageCache::add(const string &id, const TImageP &img, bool overwrite) +{ + if (!isEnabled()) + return; + m_imp->add(id, img, overwrite); +} + +//------------------------------------------------------------------------------ + +void TImageCache::Imp::add(const string &id, const TImageP &img, bool overwrite) +{ + + TThread::MutexLocker sl(&m_mutex); + +#ifdef LEVO + std::map::iterator it1 = m_uncompressedItems.begin(); + + for (; it1 != m_uncompressedItems.end(); ++it1) { + UncompressedOnMemoryCacheItemP item = (UncompressedOnMemoryCacheItemP)it1->second; + //m_memUsage -= item->getSize(); + assert(item); + TImageP refImg = item->getImage(); + if (refImg.getPointer() == img.getPointer() && it1->first != id) + assert(!"opps gia' esiste in cache!"); + } +#endif + + std::map::iterator itUncompr = m_uncompressedItems.find(id); + std::map::iterator itCompr = m_compressedItems.find(id); + +#ifdef _DEBUGTOONZ + TRasterImageP rimg = (TRasterImageP)img; + TToonzImageP timg = (TToonzImageP)img; +#endif + + if (itUncompr != m_uncompressedItems.end() || + itCompr != m_compressedItems.end()) //already present in cache with same id... + { + if (overwrite) { +#ifdef _DEBUGTOONZ + if (rimg) + rimg->getRaster()->m_cashed = true; + else if (timg) + timg->getRaster()->m_cashed = true; +#endif + std::map::iterator it; + + if (itUncompr != m_uncompressedItems.end()) { + assert(m_itemHistory.find(itUncompr->second->m_historyCount) != m_itemHistory.end()); + m_itemHistory.erase(itUncompr->second->m_historyCount); + m_itemsByImagePointer.erase(getPointer(itUncompr->second->getImage())); + m_uncompressedItems.erase(itUncompr); + } + if (itCompr != m_compressedItems.end()) + m_compressedItems.erase(id); + } else + return; + } else { + std::map::iterator dt = m_duplicatedItems.find(id); + if ((dt != m_duplicatedItems.end()) && !overwrite) + return; + + std::map::iterator it; + if ((it = m_itemsByImagePointer.find(getPointer(img))) != m_itemsByImagePointer.end()) //already present in cache with another id... + { + m_duplicatedItems[id] = it->second; + return; + } + + if (dt != m_duplicatedItems.end()) + m_duplicatedItems.erase(dt); + } + + CacheItemP item; + +#ifdef _DEBUGTOONZ + if (rimg) + rimg->getRaster()->m_cashed = true; + else if (timg) + timg->getRaster()->m_cashed = true; +#endif + + item = new UncompressedOnMemoryCacheItem(img); +#ifdef TNZCORE_LIGHT + item->m_cantCompress = false; +#else + item->m_cantCompress = (TVectorImageP(img) ? true : false); +#endif + item->m_id = id; + m_uncompressedItems[id] = item; + m_itemsByImagePointer[getPointer(img)] = id; + item->m_historyCount = HistoryCount; + m_itemHistory[HistoryCount] = id; + HistoryCount++; + + doCompress(); + +#ifdef _DEBUGTOONZ +//int itemCount = m_imp->m_uncompressedItems.size()+m_imp->m_compressedItems.size(); +//m_imp->outputDebug(); +#endif +} + +void TImageCache::remove(const string &id) +{ + m_imp->remove(id); +} + +//------------------------------------------------------------------------------ + +void TImageCache::Imp::remove(const string &id) +{ + if (CacheInstance == 0) + return; //the remove can be called when exiting from toonz...after the imagecache was already freed! + + assert(check == magic); + TThread::MutexLocker sl(&m_mutex); + + std::map::iterator it1; + if ((it1 = m_duplicatedItems.find(id)) != m_duplicatedItems.end()) //it's a duplicated id... + { + m_duplicatedItems.erase(it1); + return; + } + + for (it1 = m_duplicatedItems.begin(); it1 != m_duplicatedItems.end(); ++it1) + if (it1->second == id) + break; + + if (it1 != m_duplicatedItems.end()) //it has duplicated, so cannot erase it; I erase the duplicate, and assign its id has the main id + { + string sonId = it1->first; + m_duplicatedItems.erase(it1); + remap(sonId, id); + return; + } + + std::map::iterator it = m_uncompressedItems.find(id); + std::map::iterator itc = m_compressedItems.find(id); + if (it != m_uncompressedItems.end()) { + const CacheItemP &item = it->second; + assert((UncompressedOnMemoryCacheItemP)item); + assert(m_itemHistory.find(it->second->m_historyCount) != m_itemHistory.end()); + m_itemHistory.erase(it->second->m_historyCount); + m_itemsByImagePointer.erase(getPointer(it->second->getImage())); + +#ifdef _DEBUGTOONZ + if ((TRasterImageP)it->second->getImage()) + ((TRasterImageP)it->second->getImage())->getRaster()->m_cashed = false; + else if ((TToonzImageP)it->second->getImage()) + ((TToonzImageP)it->second->getImage())->getRaster()->m_cashed = false; +#endif + + m_uncompressedItems.erase(it); + } + if (itc != m_compressedItems.end()) + m_compressedItems.erase(itc); +} + +//------------------------------------------------------------------------------ + +void TImageCache::remap(const string &dstId, const string &srcId) +{ + m_imp->remap(dstId, srcId); +} + +void TImageCache::Imp::remap(const string &dstId, const string &srcId) +{ + + TThread::MutexLocker sl(&m_mutex); + std::map::iterator it = m_uncompressedItems.find(srcId); + if (it != m_uncompressedItems.end()) { + CacheItemP citem = it->second; + assert(m_itemHistory.find(citem->m_historyCount) != m_itemHistory.end()); + m_itemHistory.erase(citem->m_historyCount); + m_itemsByImagePointer.erase(getPointer(citem->getImage())); + m_uncompressedItems.erase(it); + + m_uncompressedItems[dstId] = citem; + m_itemHistory[citem->m_historyCount] = dstId; + m_itemsByImagePointer[getPointer(citem->getImage())] = dstId; + } + it = m_compressedItems.find(srcId); + if (it != m_compressedItems.end()) { + CacheItemP citem = it->second; + m_compressedItems.erase(it); + m_compressedItems[dstId] = citem; + } + std::map::iterator it2 = m_duplicatedItems.find(srcId); + if (it2 != m_duplicatedItems.end()) { + string id = it2->second; + m_duplicatedItems.erase(it2); + m_duplicatedItems[dstId] = id; + } + for (it2 = m_duplicatedItems.begin(); it2 != m_duplicatedItems.end(); ++it2) + if (it2->second == srcId) + it2->second = dstId; +} + +//------------------------------------------------------------------------------ + +void TImageCache::remapIcons(const string &dstId, const string &srcId) +{ + std::map::iterator it; + std::map table; + string prefix = srcId + ":"; + int j = (int)prefix.length(); + for (it = m_imp->m_uncompressedItems.begin(); it != m_imp->m_uncompressedItems.end(); ++it) { + string id = it->first; + if (id.find(prefix) == 0) + table[id] = dstId + ":" + id.substr(j); + } + for (std::map::iterator it2 = table.begin(); + it2 != table.end(); ++it2) { + remap(it2->second, it2->first); + } +} + +//------------------------------------------------------------------------------ + +void TImageCache::clear(bool deleteFolder) +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + m_imp->m_uncompressedItems.clear(); + m_imp->m_itemHistory.clear(); + m_imp->m_compressedItems.clear(); + m_imp->m_duplicatedItems.clear(); + m_imp->m_itemsByImagePointer.clear(); + if (deleteFolder && m_imp->m_rootDir != TFilePath()) + TSystem::rmDirTree(m_imp->m_rootDir); +} + +//------------------------------------------------------------------------------ + +void TImageCache::clearSceneImages() +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + + //note the ';' - which follows ':' in the ascii table + m_imp->m_uncompressedItems.erase(m_imp->m_uncompressedItems.begin(), m_imp->m_uncompressedItems.lower_bound("$:")); + m_imp->m_uncompressedItems.erase(m_imp->m_uncompressedItems.lower_bound("$;"), m_imp->m_uncompressedItems.end()); + + m_imp->m_compressedItems.erase(m_imp->m_compressedItems.begin(), m_imp->m_compressedItems.lower_bound("$:")); + m_imp->m_compressedItems.erase(m_imp->m_compressedItems.lower_bound("$;"), m_imp->m_compressedItems.end()); + + m_imp->m_duplicatedItems.erase(m_imp->m_duplicatedItems.begin(), m_imp->m_duplicatedItems.lower_bound("$:")); + m_imp->m_duplicatedItems.erase(m_imp->m_duplicatedItems.lower_bound("$;"), m_imp->m_duplicatedItems.end()); + + //Clear maps whose id is on the second of map pairs. + + std::map::iterator it; + for (it = m_imp->m_itemHistory.begin(); it != m_imp->m_itemHistory.end();) { + if (it->second.size() >= 2 && it->second[0] == '$' && it->second[1] == ':') + ++it; + else { + std::map::iterator app = it; + app++; + m_imp->m_itemHistory.erase(it); + it = app; + } + } + + std::map::iterator jt; + for (jt = m_imp->m_itemsByImagePointer.begin(); jt != m_imp->m_itemsByImagePointer.end();) { + if (jt->second.size() >= 2 && jt->second[0] == '$' && jt->second[1] == ':') + ++jt; + else { + std::map::iterator app = jt; + app++; + m_imp->m_itemsByImagePointer.erase(jt); + jt = app; + } + } +} + +//------------------------------------------------------------------------------ + +bool TImageCache::isCached(const string &id) const +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + return (m_imp->m_uncompressedItems.find(id) != m_imp->m_uncompressedItems.end() || + m_imp->m_compressedItems.find(id) != m_imp->m_compressedItems.end() || + m_imp->m_duplicatedItems.find(id) != m_imp->m_duplicatedItems.end()); +} + +//------------------------------------------------------------------------------ +#ifdef LEVO + +bool TImageCache::getSize(const string &id, TDimension &size) const +{ + QMutexLocker sl(&m_imp->m_mutex); + + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) { + + UncompressedOnMemoryCacheItemP uncompressed = it->second; + assert(uncompressed); + TToonzImageP ti = uncompressed->getImage(); + if (ti) { + size = ti->getSize(); + return true; + } + + TRasterImageP ri = uncompressed->getImage(); + if (ri && ri->getRaster()) { + size = ri->getRaster()->getSize(); + return true; + } + return false; + } + std::map::iterator itc = m_imp->m_compressedItems.find(id); + if (itc == m_imp->m_compressedItems.end()) + return false; + CacheItemP cacheItem = itc->second; + + if (cacheItem->m_imageInfo) { + RasterImageInfo *rimageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (rimageInfo) { + size = rimageInfo->m_size; + return true; + } + ToonzImageInfo *timageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (timageInfo) { + size = timageInfo->m_size; + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ + +bool TImageCache::getSavebox(const string &id, TRect &savebox) const +{ + QMutexLocker sl(&m_imp->m_mutex); + + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) { + + UncompressedOnMemoryCacheItemP uncompressed = it->second; + assert(uncompressed); + + TToonzImageP ti = uncompressed->getImage(); + if (ti) { + savebox = ti->getSavebox(); + return true; + } + TRasterImageP ri = uncompressed->getImage(); + if (ri) { + savebox = ri->getSavebox(); + return true; + } + return false; + } + std::map::iterator itc = m_imp->m_compressedItems.find(id); + if (itc == m_imp->m_compressedItems.end()) + return false; + + CacheItemP cacheItem = itc->second; + + assert(cacheItem->m_imageInfo); + + RasterImageInfo *rimageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (rimageInfo) { + savebox = rimageInfo->m_savebox; + return true; + } +#ifndef TNZCORE_LIGHT + ToonzImageInfo *timageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (timageInfo) { + savebox = timageInfo->m_savebox; + return true; + } +#endif + return false; +} + +//------------------------------------------------------------------------------ + +bool TImageCache::getDpi(const string &id, double &dpiX, double &dpiY) const +{ + QMutexLocker sl(&m_imp->m_mutex); + + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) { + + UncompressedOnMemoryCacheItemP uncompressed = it->second; + assert(uncompressed); + TToonzImageP ti = uncompressed->getImage(); + if (ti) { + ti->getDpi(dpiX, dpiY); + return true; + } + TRasterImageP ri = uncompressed->getImage(); + if (ri) { + ri->getDpi(dpiX, dpiY); + return true; + } + return false; + } + std::map::iterator itc = m_imp->m_compressedItems.find(id); + if (itc == m_imp->m_compressedItems.end()) + return false; + CacheItemP cacheItem = itc->second; + assert(cacheItem->m_imageInfo); + + RasterImageInfo *rimageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (rimageInfo) { + dpiX = rimageInfo->m_dpix; + dpiY = rimageInfo->m_dpiy; + return true; + } +#ifndef TNZCORE_LIGHT + ToonzImageInfo *timageInfo = dynamic_cast(cacheItem->m_imageInfo); + if (timageInfo) { + dpiX = timageInfo->m_dpix; + dpiY = timageInfo->m_dpiy; + return true; + } +#endif + return false; +} + +//------------------------------------------------------------------------------ +#endif + +bool TImageCache::getSubsampling(const string &id, int &subs) const +{ + + TThread::MutexLocker sl(&m_imp->m_mutex); + + std::map::iterator it1; + if ((it1 = m_imp->m_duplicatedItems.find(id)) != m_imp->m_duplicatedItems.end()) { + assert(m_imp->m_duplicatedItems.find(it1->second) == m_imp->m_duplicatedItems.end()); + return getSubsampling(it1->second, subs); + } + + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) { + UncompressedOnMemoryCacheItemP uncompressed = it->second; + assert(uncompressed); +#ifndef TNZCORE_LIGHT + if (TToonzImageP ti = uncompressed->getImage()) { + subs = ti->getSubsampling(); + return true; + } + + else +#endif + if (TRasterImageP ri = uncompressed->getImage()) { + subs = ri->getSubsampling(); + return true; + } else + return false; + } + std::map::iterator itc = m_imp->m_compressedItems.find(id); + if (itc == m_imp->m_compressedItems.end()) + return false; + CacheItemP cacheItem = itc->second; + assert(cacheItem->m_imageInfo); + if (RasterImageInfo *rimageInfo = dynamic_cast(cacheItem->m_imageInfo)) { + subs = rimageInfo->m_subs; + return true; + } +#ifndef TNZCORE_LIGHT + else if (ToonzImageInfo *timageInfo = dynamic_cast(cacheItem->m_imageInfo)) { + subs = timageInfo->m_subs; + return true; + } +#endif + else + return false; +} + +//------------------------------------------------------------------------------ + +bool TImageCache::hasBeenModified(const string &id, bool reset) const +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + + std::map::iterator it; + if ((it = m_imp->m_duplicatedItems.find(id)) != m_imp->m_duplicatedItems.end()) { + assert(m_imp->m_duplicatedItems.find(it->second) == m_imp->m_duplicatedItems.end()); + return hasBeenModified(it->second, reset); + } + + TImageP img; + + std::map::iterator itu = m_imp->m_uncompressedItems.find(id); + if (itu != m_imp->m_uncompressedItems.end()) { + if (reset && itu->second->m_modified) { + itu->second->m_modified = false; + return true; + } else + return itu->second->m_modified; + } + return true; //not present in cache==modified (for particle purposes...) +} + +//------------------------------------------------------------------------------ + +TImageP TImageCache::get(const string &id, bool toBeModified) const +{ + return m_imp->get(id, toBeModified); +} + +//------------------------------------------------------------------------------ + +TImageP TImageCache::Imp::get(const string &id, bool toBeModified) +{ + TThread::MutexLocker sl(&m_mutex); + + std::map::const_iterator it; + if ((it = m_duplicatedItems.find(id)) != m_duplicatedItems.end()) { + assert(m_duplicatedItems.find(it->second) == m_duplicatedItems.end()); + return get(it->second, toBeModified); + } + + TImageP img; + + std::map::iterator itu = m_uncompressedItems.find(id); + if (itu != m_uncompressedItems.end()) { + img = itu->second->getImage(); + if (itu->second->m_historyCount != HistoryCount - 1) //significa che l'ultimo get non era sulla stessa immagine, quindi serve aggiornare l'history! + { + assert(m_itemHistory.find(itu->second->m_historyCount) != m_itemHistory.end()); + m_itemHistory.erase(itu->second->m_historyCount); + m_itemHistory[HistoryCount] = id; + itu->second->m_historyCount = HistoryCount; + HistoryCount++; + } + if (toBeModified) { + itu->second->m_modified = true; + std::map::iterator itc = m_compressedItems.find(id); + if (itc != m_compressedItems.end()) + m_compressedItems.erase(itc); + } + return img; + } + + std::map::iterator itc = m_compressedItems.find(id); + if (itc == m_compressedItems.end()) + return 0; + + CacheItemP cacheItem = itc->second; + + img = cacheItem->getImage(); + + CacheItemP uncompressed; + uncompressed = new UncompressedOnMemoryCacheItem(img); + m_uncompressedItems[itc->first] = uncompressed; + m_itemsByImagePointer[getPointer(img)] = itc->first; + + m_itemHistory[HistoryCount] = itc->first; + uncompressed->m_historyCount = HistoryCount; + HistoryCount++; + + if (CompressedOnMemoryCacheItemP(cacheItem)) + //l'immagine compressa non la tengo insieme alla + //uncompressa se e' troppo grande + { + if (10 * cacheItem->getSize() > uncompressed->getSize()) { + m_compressedItems.erase(itc); + itc = m_compressedItems.end(); + } + } else + assert((CompressedOnDiskCacheItemP)cacheItem || (UncompressedOnDiskCacheItemP)cacheItem); //deve essere compressa! + + if (toBeModified && itc != m_compressedItems.end()) { + uncompressed->m_modified = true; + m_compressedItems.erase(itc); + } + + uncompressed->m_cantCompress = toBeModified; + // se la memoria utilizzata e' superiore al massimo consentito, comprime + doCompress(); + + uncompressed->m_cantCompress = false; + +//#define DO_MEMCHECK +#ifdef DO_MEMCHECK + assert(_CrtCheckMemory()); +#endif + + return img; +} + +//------------------------------------------------------------------------------ + +namespace +{ + +class AccumulateMemUsage +{ +public: + int operator()(int oldValue, std::pair item) + { + return oldValue + item.second->getSize(); + } +}; +} + +UINT TImageCache::getMemUsage() const +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + + int ret = std::accumulate( + m_imp->m_uncompressedItems.begin(), + m_imp->m_uncompressedItems.end(), + 0, + AccumulateMemUsage()); + + return ret + std::accumulate( + m_imp->m_compressedItems.begin(), + m_imp->m_compressedItems.end(), + 0, + AccumulateMemUsage()); +} + +//------------------------------------------------------------------------------ + +UINT TImageCache::getDiskUsage() const +{ + return 0; +} + +//------------------------------------------------------------------------------ + +UINT TImageCache::getMemUsage(const string &id) const +{ + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) + return it->second->getSize(); + + it = m_imp->m_compressedItems.find(id); + if (it != m_imp->m_compressedItems.end()) + return it->second->getSize(); + return 0; +} + +//------------------------------------------------------------------------------ + +//! Returns the uncompressed image size (in KB) of the image associated with +//! passd id, or 0 if none was found. +UINT TImageCache::getUncompressedMemUsage(const string &id) const +{ + std::map::iterator it = m_imp->m_uncompressedItems.find(id); + if (it != m_imp->m_uncompressedItems.end()) + return it->second->getSize(); + + it = m_imp->m_compressedItems.find(id); + if (it != m_imp->m_compressedItems.end()) + return it->second->getSize(); + + return 0; +} + +//------------------------------------------------------------------------------ + +/* +int TImageCache::getItemCount() const +{ + return m_imp->m_uncompressedItems.size()+m_imp->m_compressedItems.size(); +} +*/ +//------------------------------------------------------------------------------ + +UINT TImageCache::getDiskUsage(const string &id) const +{ + return 0; +} + +//------------------------------------------------------------------------------ + +void TImageCache::dump(ostream &os) const +{ + os << "mem: " << getMemUsage() << std::endl; + std::map::iterator it = m_imp->m_uncompressedItems.begin(); + for (; it != m_imp->m_uncompressedItems.end(); ++it) { + os << it->first << std::endl; + } +} + +//------------------------------------------------------------------------------ + +void TImageCache::outputMap(UINT chunkRequested, string filename) +{ + m_imp->outputMap(chunkRequested, filename); +} + +//------------------------------------------------------------------------------ + +void TImageCache::Imp::outputMap(UINT chunkRequested, string filename) +{ + TThread::MutexLocker sl(&m_mutex); + //#ifdef _DEBUG + //static int Count = 0; + + std::string st = filename /*+toString(Count++)*/ + ".txt"; + TFilePath fp(st); + Tofstream os(fp); + + int umcount1 = 0; + int umcount2 = 0; + int umcount3 = 0; + int cmcount = 0; + int cdcount = 0; + int umcount = 0; + int udcount = 0; + + TUINT64 umsize1 = 0; + TUINT64 umsize2 = 0; + TUINT64 umsize3 = 0; + TUINT64 cmsize = 0; + TUINT64 cdsize = 0; + TUINT64 umsize = 0; + TUINT64 udsize = 0; + + std::map::iterator itu = m_uncompressedItems.begin(); + + for (; itu != m_uncompressedItems.end(); ++itu) { + UncompressedOnMemoryCacheItemP uitem = itu->second; + if (uitem->m_image && hasExternalReferences(uitem->m_image)) { + umcount1++; + umsize1 += (TUINT64)(itu->second->getSize() / 1024.0); + } else if (uitem->m_cantCompress) { + umcount2++; + umsize2 += (TUINT64)(itu->second->getSize() / 1024.0); + } else { + umcount3++; + umsize3 += (TUINT64)(itu->second->getSize() / 1024.0); + } + } + std::map::iterator itc = m_compressedItems.begin(); + for (; itc != m_compressedItems.end(); ++itc) { + CacheItemP boh = itc->second; + CompressedOnMemoryCacheItemP cmitem = itc->second; + CompressedOnDiskCacheItemP cditem = itc->second; + UncompressedOnDiskCacheItemP uditem = itc->second; + if (cmitem) { + cmcount++; + cmsize += cmitem->getSize(); + } else if (cditem) { + cdcount++; + cdsize += cditem->getSize(); + } else { + assert(uditem); + udcount++; + udsize += uditem->getSize(); + } + } + + TUINT64 currPhisMemoryAvail = (TUINT64)(TSystem::getFreeMemorySize(true) / 1024.0); + //TUINT64 currVirtualMemoryAvail = TSystem::getFreeMemorySize(false)/1024.0; + + os << "************************************************************\n"; + + os << "***requested memory: " + toString((int)chunkRequested / 1048576.0) + " MB\n"; + //os<<"*** memory in rasters: " + toString((int)TRaster::getTotalMemoryInKB()/1024.0) + " MB\n"; + //os<<"***virtualmem " + toString((int)currVirtualMemoryAvail) + " MB\n"; + os << "***phismem " + toString((int)currPhisMemoryAvail) + " MB; percent of tot:" + toString((int)((currPhisMemoryAvail * 100) / m_reservedMemory)) + "\n"; + //os<<"***bigmem available" + toString((int)TBigMemoryManager::instance()->getAvailableMemoryinKb()); + os << "***uncompressed NOT compressable(refcount>1) " + toString(umcount1) + " " + toString(umsize1 / 1024.0) + " MB\n"; + os << "***uncompressed NOT compressable(cantCompress) " + toString(umcount2) + " " + toString(umsize2 / 1024.0) + " MB\n"; + os << "***uncompressed compressable " + toString(umcount3) + " " + toString(umsize3 / 1024.0) + " MB\n"; + os << "***compressed on mem " + toString(cmcount) + " " + toString((int)cmsize / 1048576.0) + " MB\n"; + os << "***compressed on disk " + toString(cdcount) + " " + toString((int)cdsize / 1048576.0) + " MB\n"; + os << "***uncompressed on disk " + toString(udcount) + " " + toString((int)udsize / 1048576.0) + " MB\n"; + + //TBigMemoryManager::instance()->printMap(); + + //#endif +} + +//------------------------------------------------------------------------------ + +void TImageCache::compress(const string &id) +{ + m_imp->doCompress(id); +} + +//------------------------------------------------------------------------------ + +#ifndef TNZCORE_LIGHT + +void TImageCache::add(const QString &id, const TImageP &img, bool overwrite) +{ + if (!isEnabled()) + return; + m_imp->add(id.toStdString(), img, overwrite); +} + +//------------------------------------------------------------------------------ + +void TImageCache::remove(const QString &id) +{ + m_imp->remove(id.toStdString()); +} + +//------------------------------------------------------------------------------ + +TImageP TImageCache::get(const QString &id, bool toBeModified) const +{ + return get(id.toStdString(), toBeModified); +} + +#endif + +//************************************************************************************* +// TCachedImage implementation +//************************************************************************************* + +DEFINE_CLASS_CODE(TCachedImage, 103) + +TCachedImage::TCachedImage() + : TSmartObject(m_classCode), m_ref(TImageCache::instance()->getUniqueId()) +{ +} + +//------------------------------------------------------------------------------ + +TCachedImage::TCachedImage(const TImageP &img) + : TSmartObject(m_classCode), m_ref(TImageCache::instance()->getUniqueId()) +{ + setImage(img); +} + +//------------------------------------------------------------------------------ + +TCachedImage::~TCachedImage() +{ + TImageCache::instance()->remove(m_ref); +} + +//------------------------------------------------------------------------------ + +void TCachedImage::setImage(const TImageP &img, bool overwrite) +{ + TImageCache::instance()->add(m_ref, img, overwrite); +} + +//------------------------------------------------------------------------------ + +TImageP TCachedImage::image(bool toBeModified) +{ + return TImageCache::instance()->get(m_ref, toBeModified); +} diff --git a/toonz/sources/common/tcolor/tcolorfunctions.cpp b/toonz/sources/common/tcolor/tcolorfunctions.cpp new file mode 100644 index 0000000..fddfeee --- /dev/null +++ b/toonz/sources/common/tcolor/tcolorfunctions.cpp @@ -0,0 +1,86 @@ + + +#include "tcolorfunctions.h" +#include "tpixelutils.h" + +TPixel32 TColorFader::operator()(const TPixel32 &color) const +{ + TPixel32 ret = blend(color, m_color, m_fade); + return ret; +} + +bool TColorFader::getParameters(Parameters &p) const +{ + p.m_mR = p.m_mG = p.m_mB = p.m_mM = (1 - m_fade); + p.m_cR = m_fade * m_color.r; + p.m_cG = m_fade * m_color.g; + p.m_cB = m_fade * m_color.b; + p.m_cM = m_fade * m_color.m; + return true; +} + +//--------------------------------------- + +TGenericColorFunction::TGenericColorFunction(const double m[4], const double c[4]) +{ + memcpy(m_m, m, 4 * sizeof(double)); + memcpy(m_c, c, 4 * sizeof(double)); +} + +//--------------------------------------- + +TPixel32 TGenericColorFunction::operator()(const TPixel32 &color) const +{ + return TPixel32(tcrop(m_m[0] * color.r + m_c[0], 0.0, 255.0), + tcrop(m_m[1] * color.g + m_c[1], 0.0, 255.0), + tcrop(m_m[2] * color.b + m_c[2], 0.0, 255.0), + tcrop(m_m[3] * color.m + m_c[3], 0.0, 255.0)); +} + +//--------------------------------------- + +bool TGenericColorFunction::getParameters(Parameters &p) const +{ + p.m_mR = m_m[0], p.m_mG = m_m[1], p.m_mB = m_m[2], p.m_mM = m_m[3]; + p.m_cR = m_c[0], p.m_cG = m_c[1], p.m_cB = m_c[2], p.m_cM = m_c[3]; + + return true; +} + +//--------------------------------------- + +TPixel32 TTranspFader::operator()(const TPixel32 &color) const +{ + return TPixel32(color.r, color.g, color.b, m_transp * color.m); +} + +//--------------------------------------- + +bool TTranspFader::getParameters(Parameters &p) const +{ + assert(false); + return true; +} + +//---------------------------------- + +TPixel32 TOnionFader::operator()(const TPixel32 &color) const +{ + if (color.m == 0) + return color; + + TPixel32 blendColor = blend(color, m_color, m_fade); + blendColor.m = 180; + return blendColor; +} + +bool TOnionFader::getParameters(Parameters &p) const +{ + p.m_mR = p.m_mG = p.m_mB = (1 - m_fade); + p.m_mM = 1; + p.m_cR = m_fade * m_color.r; + p.m_cG = m_fade * m_color.g; + p.m_cB = m_fade * m_color.b; + p.m_cM = m_color.m; + return true; +} diff --git a/toonz/sources/common/tcolor/tcolorvalue.cpp b/toonz/sources/common/tcolor/tcolorvalue.cpp new file mode 100644 index 0000000..5f9101b --- /dev/null +++ b/toonz/sources/common/tcolor/tcolorvalue.cpp @@ -0,0 +1,185 @@ + + +#include "tcolorvalue.h" + +//=================================================================== + +void TColorValue::getHsv(int &ih, int &is, int &iv) const +{ + double max, min; + double delta; + double r, g, b; + double v, s, h; + r = m_r; + g = m_g; + b = m_b; + assert(0 <= r && r <= 1); + assert(0 <= g && g <= 1); + assert(0 <= b && b <= 1); + + max = tmax(r, g, b); + min = tmin(r, g, b); + + v = max; + + if (max != 0) + s = (max - min) / max; + else + s = 0; + + if (s == 0) + h = 0; + else { + delta = max - min; + + if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g) / delta; + h = h * 60; + if (h < 0) + h += 360; + } + assert(0 <= h && h <= 360); + assert(0 <= s && s <= 1); + assert(0 <= v && v <= 1); + ih = (int)h; + is = (int)(s * 100); + iv = (int)(v * 100); +} + +//=================================================================== + +void TColorValue::getHls(double &h, double &l, double &s) const +{ + double max, min; + double delta; + + max = tmax(m_r, m_g, m_b); + min = tmin(m_r, m_g, m_b); + + l = (max + min) / 2; + + if (max == min) { + s = 0; + h = 0; + } else { + if (l <= 0.5) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + + delta = max - min; + if (m_r == max) + h = (m_g - m_b) / delta; + else if (m_g == max) + h = 2 + (m_b - m_r) / delta; + else if (m_b == max) + h = 4 + (m_r - m_g) / delta; + + h = h * 60; + if (h < 0) + h += 360; + } +} + +//=================================================================== + +void TColorValue::setHsv(int h, int s, int v) +{ + int i; + double p, q, t, f; + double hue, sat, value; + + hue = h; + sat = 0.01 * s; + value = 0.01 * v; + + assert(0 <= hue && hue <= 360); + assert(0 <= sat && sat <= 1); + assert(0 <= value && value <= 1); + + if (sat == 0) { + m_r = m_g = m_b = value; + } else { + if (hue == 360) + hue = 0; + + hue = hue / 60; + i = (int)hue; + f = hue - i; + p = tcrop(value * (1 - sat), 0., 1.); + q = tcrop(value * (1 - (sat * f)), 0., 1.); + t = tcrop(value * (1 - (sat * (1 - f))), 0., 1.); + + switch (i) { + case 0: + m_r = value; + m_g = t; + m_b = p; + break; + case 1: + m_r = q; + m_g = value; + m_b = p; + break; + case 2: + m_r = p; + m_g = value; + m_b = t; + break; + case 3: + m_r = p; + m_g = q; + m_b = value; + break; + case 4: + m_r = t; + m_g = p; + m_b = value; + break; + case 5: + m_r = value; + m_g = p; + m_b = q; + break; + } + } +} + +//=================================================================== + +void TColorValue::getRgb(int &r, int &g, int &b) const +{ + r = (int)(m_r * 255 + 0.5); + g = (int)(m_g * 255 + 0.5); + b = (int)(m_b * 255 + 0.5); +} + +//=================================================================== + +TPixel32 TColorValue::getPixel() const +{ + int r, g, b; + getRgb(r, g, b); + return TPixel32(r, g, b, (int)(m_m * 255.0 + 0.5)); +} + +//=================================================================== + +void TColorValue::setRgb(int r, int g, int b) +{ + m_r = r / 255.0; + m_g = g / 255.0; + m_b = b / 255.0; +} + +//=================================================================== + +void TColorValue::setPixel(const TPixel32 &src) +{ + setRgb(src.r, src.g, src.b); + m_m = src.m / 255.0; +} diff --git a/toonz/sources/common/tcolor/tpixel.cpp b/toonz/sources/common/tcolor/tpixel.cpp new file mode 100644 index 0000000..3cf7a82 --- /dev/null +++ b/toonz/sources/common/tcolor/tpixel.cpp @@ -0,0 +1,132 @@ + + +#include "tpixel.h" +#include "tpixelgr.h" + +const int TPixelRGBM32::maxChannelValue = 0xff; +const int TPixelRGBM64::maxChannelValue = 0xffff; +const int TPixelGR8::maxChannelValue = 0xff; +const int TPixelGR16::maxChannelValue = 0xffff; + +const TPixelRGBM32 TPixelRGBM32::Red(maxChannelValue, 0, 0); +const TPixelRGBM32 TPixelRGBM32::Green(0, maxChannelValue, 0); +const TPixelRGBM32 TPixelRGBM32::Blue(0, 0, maxChannelValue); +const TPixelRGBM32 TPixelRGBM32::Yellow(maxChannelValue, maxChannelValue, 0); +const TPixelRGBM32 TPixelRGBM32::Cyan(0, maxChannelValue, maxChannelValue); +const TPixelRGBM32 TPixelRGBM32::Magenta(maxChannelValue, 0, maxChannelValue); +const TPixelRGBM32 TPixelRGBM32::White(maxChannelValue, maxChannelValue, maxChannelValue); +const TPixelRGBM32 TPixelRGBM32::Black(0, 0, 0); +const TPixelRGBM32 TPixelRGBM32::Transparent(0, 0, 0, 0); +//--------------------------------------------------- + +const TPixelRGBM64 TPixelRGBM64::Red(maxChannelValue, 0, 0); +const TPixelRGBM64 TPixelRGBM64::Green(0, maxChannelValue, 0); +const TPixelRGBM64 TPixelRGBM64::Blue(0, 0, maxChannelValue); +const TPixelRGBM64 TPixelRGBM64::Yellow(maxChannelValue, maxChannelValue, 0); +const TPixelRGBM64 TPixelRGBM64::Cyan(0, maxChannelValue, maxChannelValue); +const TPixelRGBM64 TPixelRGBM64::Magenta(maxChannelValue, 0, maxChannelValue); +const TPixelRGBM64 TPixelRGBM64::White(maxChannelValue, maxChannelValue, maxChannelValue); +const TPixelRGBM64 TPixelRGBM64::Black(0, 0, 0); +const TPixelRGBM64 TPixelRGBM64::Transparent(0, 0, 0, 0); +//--------------------------------------------------- +const TPixelD TPixelD::Red(1, 0, 0); +const TPixelD TPixelD::Green(0, 1, 0); +const TPixelD TPixelD::Blue(0, 0, 1); +const TPixelD TPixelD::Yellow(1, 1, 0); +const TPixelD TPixelD::Cyan(0, 1, 1); +const TPixelD TPixelD::Magenta(1, 0, 1); +const TPixelD TPixelD::White(1, 1, 1); +const TPixelD TPixelD::Black(0, 0, 0); +const TPixelD TPixelD::Transparent(0, 0, 0, 0); +//--------------------------------------------------- +const TPixelGR8 TPixelGR8::White(maxChannelValue); +const TPixelGR8 TPixelGR8::Black(0); + +const TPixelGR16 TPixelGR16::White(maxChannelValue); +const TPixelGR16 TPixelGR16::Black(0); + +ostream &operator<<(ostream &out, const TPixel32 &pixel) +{ + return out << "PixRGBM32(" + << (int)pixel.r << ", " << (int)pixel.g << ", " + << (int)pixel.b << ", " << (int)pixel.m << ")"; +} + +ostream &operator<<(ostream &out, const TPixel64 &pixel) +{ + return out + << "PixRGBM64(" << pixel.r << ", " << pixel.g + << ", " << pixel.b << ", " << pixel.m << ")"; +} + +ostream &operator<<(ostream &out, const TPixelD &pixel) +{ + return out + << "PixD(" << pixel.r << ", " << pixel.g << ", " + << pixel.b << ", " << pixel.m << ")"; +} + +//============================================================================= + +/* +TPixel32 DVAPI TPixel32::from(const TPixelGR8 &pix) +{ + return TPixel32(pix.value, pix.value, pix.value, maxChannelValue); +} + +//----------------------------------------------------------------------------- + +TPixel32 DVAPI TPixel32::from(const TPixelGR16 &pix) +{ + UCHAR value = byteFromUshort(pix.value); + return TPixel32(value, value, value, maxChannelValue); +} + +//----------------------------------------------------------------------------- + +TPixelD DVAPI TPixelD::from(const TPixelGR8 &pix) +{ + double v = (double)pix.value * (1.0/255.0); + return TPixelD(v,v,v,v); +} + +//----------------------------------------------------------------------------- + + + +TPixelD DVAPI TPixelD::from(const TPixelGR16 &pix) +{ + double v = (double)pix.value * (1.0/65535.0); + return TPixelD(v,v,v,v); +} + +*/ +//----------------------------------------------------------------------------- + +//TPixelGR8 TPixelGR8::from(const TPixelD &pix) +//{ +// return from(TPixel32::from(pix)); +//} + +//----------------------------------------------------------------------------- + +TPixelGR8 DVAPI TPixelGR8::from(const TPixel32 &pix) +{ + return TPixelGR8((((UINT)(pix.r) * 19594 + + (UINT)(pix.g) * 38472 + + (UINT)(pix.b) * 7470 + + (UINT)(1 << 15)) >> + 16)); +} + +//----------------------------------------------------------------------------- +TPixelGR16 DVAPI TPixelGR16::from(const TPixel64 &pix) +{ + return TPixelGR16((((UINT)(pix.r) * 19594 + + (UINT)(pix.g) * 38472 + + (UINT)(pix.b) * 7470 + + (UINT)(1 << 15)) >> + 16)); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tcolor/tpixelutils.cpp b/toonz/sources/common/tcolor/tpixelutils.cpp new file mode 100644 index 0000000..f9b46ec --- /dev/null +++ b/toonz/sources/common/tcolor/tpixelutils.cpp @@ -0,0 +1,698 @@ + + +#include "tpixelutils.h" + +// /*!This method is used to produce current palette colors for cm24 Toonz images.*/ +// static inline TPixelRGBM32 combine(const TPixelRGBM32 &a, const TPixelRGBM32 &b) { +// return (*(TUINT32*)&a + *(TUINT32*)&b); +// } + +//----------------------------------------------------------------------------- + +namespace +{ +int byteCrop(int v) +{ + return (unsigned int)v <= 255 ? v : v > 255 ? 255 : 0; +} +int wordCrop(int v) +{ + return (unsigned int)v <= 65535 ? v : v > 65535 ? 65535 : 0; +} +} // namespace + +//----------------------------------------------------------------------------- + +void hsv2rgb(TPixel32 &dstRgb, int srcHsv[3], int maxHsv) +{ + int i; + double p, q, t, f; + double hue, sat, value; + assert(maxHsv); + hue = ((double)srcHsv[0] / maxHsv) * 360.; + sat = (double)srcHsv[1] / maxHsv; + value = (double)srcHsv[2] / maxHsv; + + if (hue > 360) + hue -= 360; + if (hue < 0) + hue += 360; + if (sat < 0) + sat = 0; + if (sat > 1) + sat = 1; + if (value < 0) + value = 0; + if (value > 1) + value = 1; + if (sat == 0) { + dstRgb.r = dstRgb.g = dstRgb.b = tcrop((int)(value * 255), 0, 255); + } else { + if (hue == 360) + hue = 0; + + hue = hue / 60; + i = (int)hue; + f = hue - i; + p = value * (1 - sat); + q = value * (1 - (sat * f)); + t = value * (1 - (sat * (1 - f))); + + if (i == 0) { + dstRgb.r = tcrop((int)(value * 255), 0, 255); + dstRgb.g = tcrop((int)(t * 255), 0, 255); + dstRgb.b = tcrop((int)(p * 255), 0, 255); + } else if (i == 1) { + + dstRgb.r = tcrop((int)(q * 255), 0, 255); + dstRgb.g = tcrop((int)(value * 255), 0, 255); + dstRgb.b = tcrop((int)(p * 255), 0, 255); + } else if (i == 2) { + dstRgb.r = tcrop((int)(p * 255), 0, 255); + dstRgb.g = tcrop((int)(value * 255), 0, 255); + dstRgb.b = tcrop((int)(t * 255), 0, 255); + } else if (i == 3) { + dstRgb.r = tcrop((int)(p * 255), 0, 255); + dstRgb.g = tcrop((int)(q * 255), 0, 255); + dstRgb.b = tcrop((int)(value * 255), 0, 255); + } else if (i == 4) { + dstRgb.r = tcrop((int)(t * 255), 0, 255); + dstRgb.g = tcrop((int)(p * 255), 0, 255); + dstRgb.b = tcrop((int)(value * 255), 0, 255); + } else if (i == 5) { + dstRgb.r = tcrop((int)(value * 255), 0, 255); + dstRgb.g = tcrop((int)(p * 255), 0, 255); + dstRgb.b = tcrop((int)(q * 255), 0, 255); + } + } + dstRgb.m = 255; //matte +} +//----------------------------------------------------------------------------- + +void HSV2RGB(double hue, double sat, double value, + double *red, double *green, double *blue) +{ + int i; + double p, q, t, f; + + // if (hue > 360 || hue < 0) + // hue=0; + if (hue > 360) + hue -= 360; + if (hue < 0) + hue += 360; + if (sat < 0) + sat = 0; + if (sat > 1) + sat = 1; + if (value < 0) + value = 0; + if (value > 1) + value = 1; + if (sat == 0) { + *red = value; + *green = value; + *blue = value; + } else { + if (hue == 360) + hue = 0; + + hue = hue / 60; + i = (int)hue; + f = hue - i; + p = value * (1 - sat); + q = value * (1 - (sat * f)); + t = value * (1 - (sat * (1 - f))); + + switch (i) { + CASE 0 : + *red = value; + *green = t; + *blue = p; + CASE 1 : + *red = q; + *green = value; + *blue = p; + CASE 2 : + *red = p; + *green = value; + *blue = t; + CASE 3 : + *red = p; + *green = q; + *blue = value; + CASE 4 : + *red = t; + *green = p; + *blue = value; + CASE 5 : + *red = value; + *green = p; + *blue = q; + } + } +} + +void RGB2HSV(double r, double g, double b, + double *h, double *s, double *v) +{ + double max, min; + double delta; + + max = tmax(r, g, b); + min = tmin(r, g, b); + + *v = max; + + if (max != 0) + *s = (max - min) / max; + else + *s = 0; + + if (*s == 0) + *h = 0; + else { + delta = max - min; + + if (r == max) + *h = (g - b) / delta; + else if (g == max) + *h = 2 + (b - r) / delta; + else if (b == max) + *h = 4 + (r - g) / delta; + *h = *h * 60; + if (*h < 0) + *h += 360; + } +} +void rgb2hsv(int dstHsv[3], const TPixel32 &srcRgb, int maxHsv) +{ + double max, min; + double delta; + double r, g, b; + double v, s, h; + r = srcRgb.r / 255.; + g = srcRgb.g / 255.; + b = srcRgb.b / 255.; + + max = tmax(r, g, b); + min = tmin(r, g, b); + + v = max; + + if (max != 0) + s = (max - min) / max; + else + s = 0; + + if (s == 0) + h = 0; + else { + delta = max - min; + + if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g) / delta; + h = h * 60; + if (h < 0) + h += 360; + } + + dstHsv[0] = tcrop((int)((h / 360.) * maxHsv), 0, maxHsv); + dstHsv[1] = tcrop((int)(s * maxHsv), 0, maxHsv); + dstHsv[2] = tcrop((int)(v * maxHsv), 0, maxHsv); +} + +/*! + Conversion between RGB and HLS colorspace +*/ + +namespace +{ +//helper function +inline double HLSValue(double n1, double n2, double h) +{ + if (h > 360) + h -= 360; + else if (h < 0) + h += 360; + if (h < 60) + return n1 + (n2 - n1) * h / 60; + else if (h < 180) + return n2; + else if (h < 240) + return n1 + (n2 - n1) * (240 - h) / 60; + else + return n1; +} +} +void HLS2RGB(double h, double l, double s, + double *r, double *g, double *b) +{ + if (s == 0) { + *r = *g = *b = l; + return; + } + + double m1, m2; + + if (l < 0.5) + m2 = l * (1 + s); + else + m2 = l + s + l * s; + m1 = 2 * l - m2; + + *r = HLSValue(m1, m2, h + 120); + *g = HLSValue(m1, m2, h); + *b = HLSValue(m1, m2, h - 120); +} + +void rgb2hls(double r, double g, double b, + double *h, double *l, double *s) + +{ + double max, min; + double delta; + + max = tmax(r, g, b); + min = tmin(r, g, b); + + *l = (max + min) / 2; + + if (max == min) { + *s = 0; + *h = 0; + } else { + if (*l <= 0.5) + *s = (max - min) / (max + min); + else + *s = (max - min) / (2 - max - min); + + delta = max - min; + if (r == max) + *h = (g - b) / delta; + else if (g == max) + *h = 2 + (b - r) / delta; + else if (b == max) + *h = 4 + (r - g) / delta; + + *h = *h * 60; + if (*h < 0) + *h += 360; + } +} + +//----------------------------------------------------------------------------- + +TPixel32 toPixel32(const TPixel64 &src) +{ + return TPixelRGBM32( + byteFromUshort(src.r), + byteFromUshort(src.g), + byteFromUshort(src.b), + byteFromUshort(src.m)); +} + +//----------------------------------------------------------------------------- + +TPixel32 toPixel32(const TPixelD &src) +{ + const double factor = 255.0; + return TPixel32( + byteCrop(tround(src.r * factor)), + byteCrop(tround(src.g * factor)), + byteCrop(tround(src.b * factor)), + byteCrop(tround(src.m * factor))); +} + +//----------------------------------------------------------------------------- + +TPixel32 toPixel32(const TPixelGR8 &src) +{ + return TPixel32(src.value, src.value, src.value); +} + +//----------------------------------------------------------------------------- + +TPixel64 toPixel64(const TPixel32 &src) +{ + return TPixelRGBM64( + ushortFromByte(src.r), + ushortFromByte(src.g), + ushortFromByte(src.b), + ushortFromByte(src.m)); +} + +//----------------------------------------------------------------------------- + +TPixel64 toPixel64(const TPixelD &src) +{ + const double factor = 65535.0; + return TPixel64( + wordCrop(tround(src.r * factor)), + wordCrop(tround(src.g * factor)), + wordCrop(tround(src.b * factor)), + wordCrop(tround(src.m * factor))); +} + +//----------------------------------------------------------------------------- + +TPixel64 toPixel64(const TPixelGR8 &src) +{ + int v = ushortFromByte(src.value); + return TPixel64(v, v, v); +} + +//----------------------------------------------------------------------------- + +TPixelD toPixelD(const TPixel32 &src) +{ + const double factor = 1.0 / 255.0; + return TPixelD(factor * src.r, factor * src.g, factor * src.b, factor * src.m); +} + +//----------------------------------------------------------------------------- + +TPixelD toPixelD(const TPixel64 &src) +{ + const double factor = 1.0 / 65535.0; + return TPixelD(factor * src.r, factor * src.g, factor * src.b, factor * src.m); +} + +//----------------------------------------------------------------------------- + +TPixelD toPixelD(const TPixelGR8 &src) +{ + const double v = (double)src.value / 255.0; + return TPixelD(v, v, v); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#ifdef CICCIO + +//----------------------------------------------------------------------------- +/*!toonz color-map, 16 bits 7+5+4 bits (paint, ink, tone)*/ +class DVAPI TPixelCM16 +{ // 754 + static const int maxChannelValue; + +public: + USHORT value; + + TPixelCM16(int v = 0) : value(v){}; + TPixelCM16(int ink, int paint, int tone) : value(tone | ink << 9 | paint << 4){}; + TPixelCM16(const TPixelCM16 &pix) : value(pix.value){}; + + inline bool operator==(const TPixelCM16 &p) const { return value == p.value; }; + + int getInkId() { return value >> 9; }; + int getPaintId() { return (value >> 4) & 0x1F; }; + int getToneId() { return value & 0xF; }; + + static TPixelCM16 pureInk(int ink) { return TPixelCM16(ink, 0, 0); }; + static TPixelCM16 purePaint(int paint) { return TPixelCM16(0, paint, 255); }; +}; + +//----------------------------------------------------------------------------- +/*!cmapped, 16 bits, standard SGI 256-color colormap */ +class DVAPI TPixelCM16S8 +{ + static const int maxChannelValue; + +public: + USHORT value; + TPixelCM16S8(int v = 0) : value(v){}; +}; + +//----------------------------------------------------------------------------- +/*cmapped, 16 bits, standard SGI+Toonz 4096-color colormap */ +class DVAPI TPixelCM16S12 +{ + static const int maxChannelValue; + +public: + USHORT value; + TPixelCM16S12(int v = 0) : value(v){}; +}; + +//----------------------------------------------------------------------------- +/*!Toonz color-map, 8+8+8 bits (col, pen, tone) + 8 msb bits extra*/ +class DVAPI TPixelCM24 +{ + static const int maxChannelValue; + +public: + /* serve m_value come membro? vincenzo */ + TUINT32 m_value; + UCHAR m_ink, m_paint, m_tone; + + TPixelCM24(int v = 0) : m_ink((v >> 8) & 0xff), m_paint((v >> 16) & 0xff), m_tone(v & 0xff){}; + TPixelCM24(int ink, int paint, int tone) : m_value(0), m_ink(ink), m_paint(paint), m_tone(tone){}; + TPixelCM24(const TPixelCM24 &pix) : m_value(pix.m_value), m_ink(pix.m_ink), m_paint(pix.m_paint), m_tone(pix.m_tone){}; + + inline bool operator==(const TPixelCM24 &p) const { return m_paint == p.m_paint && m_ink == p.m_ink && m_tone == p.m_tone; }; + //rivedere + int getPaintIdx() { return m_paint << 16 | m_tone; }; + int getInkIdx() { return m_ink << 16 | m_tone; }; + // int getTone () {return m_tone;}; m_tone e' pubblico! + + //static inline TPixelRGBM from(const TPixelCM24 &pix); + + static TPixelCM24 pureInk(int ink) { return TPixelCM24(ink, 0, 0); }; + static TPixelCM24 purePaint(int paint) { return TPixelCM24(0, paint, 255); }; +}; +//----------------------------------------------------------------------------- + +//!Toonz5.0 color-map, 12+12+8 bits (ink,paint,tone) +class DVAPI TPixelCM32 +{ + +public: + TUINT32 m_value; + + TPixelCM32(TUINT32 v = 0) : m_value{}; + TPixelCM32(int ink, int paint, int tone) + : m_value(ink << 20 | paint << 8 | tone) + { + assert(0 <= ink && ink < 4096); + assert(0 <= paint && paint < 4096); + assert(0 <= tone && tone < 256); + }; + TPixelCM32(const TPixelCM32 &pix) + : m_value(pix.m_value){}; + + inline bool operator==(const TPixelCM32 &p) const + { + return m_value == p.m_value; + }; + + inline bool operator<(const TPixelCM32 &p) const + { + return m_value < p.m_value; + }; + + int getPaint() const { return m_paint >> 16 | m_tone; }; + int getInk() const { return m_ink << 16 | m_tone; }; + int getTone() const { return m_tone; }; + + //static inline TPixelRGBM from(const TPixelCM24 &pix); + + static TPixelCM24 pureInk(int ink) { return TPixelCM24(ink, 0, 0); }; + static TPixelCM24 purePaint(int paint) { return TPixelCM24(0, paint, 255); }; +}; + +//----------------------------------------------------------------------------- +/*!RGB 5+6+5 bits, red most significant */ +class DVAPI TPixelRGB565 +{ +public: + TUINT32 r : 5; + TUINT32 g : 6; + TUINT32 b : 5; + TPixelRGB565(int rr, int gg, int bb) : r(rr), g(gg), b(bb) + { + assert(0 <= rr && rr < (1 << 5)); + assert(0 <= gg && gg < (1 << 6)); + assert(0 <= bb && bb < (1 << 5)); + }; + inline bool operator==(const TPixelRGB565 &p) const + { + return r == p.r && g == p.g && b == p.b; + }; + inline bool operator<(const TPixelRGB565 &p) const + { + return r < p.r || r == p.r && (g < p.g || g == p.g && (b < p.b)); + }; + + //!Converts TPixelCM8 into TPixelRGB565 + static inline TPixelRGB565 from(const TPixelCM8 &pix); + //!Returns itself + static inline TPixelRGB565 from(const TPixelRGB565 &pix) { return pix; }; + //!Converts TPixelRGBM32 into TPixelRGB565 + static inline TPixelRGB565 from(const TPixelRGBM32 &pix); + //!Converts TPixelRGBM64 into TPixelRGB565 + static inline TPixelRGB565 from(const TPixelRGBM64 &pix); + //!Converts TPixelGR8 into TPixelRGB565 + static inline TPixelRGB565 from(const TPixelGR8 &pix); + //!Converts TPixelGR16 into TPixelRGB565 + static inline TPixelRGB565 from(const TPixelGR16 &pix); + /* + static const TPixelRGB565 Red; + static const TPixelRGB565 Green; + static const TPixelRGB565 Blue; + static const TPixelRGB565 White; + static const TPixelRGB565 Black; +*/ +}; + +//----------------------------------------------------------------------------- + +TPixelRGBM32 DVAPI TPixelRGBM32::from(const TPixelCM8 &pix) +{ + return TPixelRGBM32(pix.value, pix.value, pix.value); +} + +//----------------------------------------------------------------------------- + +TPixelRGBM64 DVAPI TPixelRGBM64::from(const TPixelRGBM32 &pix) +{ + return TPixelRGBM64( + ushortFromByte(pix.r), + ushortFromByte(pix.g), + ushortFromByte(pix.b), + ushortFromByte(pix.m)); +} + +//----------------------------------------------------------------------------- + +TPixelRGBM32 DVAPI TPixelRGBM32::from(const TPixelRGBM64 &pix) +{ + return TPixelRGBM32( + byteFromUshort(pix.r), + byteFromUshort(pix.g), + byteFromUshort(pix.b), + byteFromUshort(pix.m)); +} + +//----------------------------------------------------------------------------- + +TPixelRGBM32 DVAPI TPixelRGBM32::from(const TPixelGR8 &pix) +{ + return TPixelRGBM32(pix.value, pix.value, pix.value, maxChannelValue); +} + +//----------------------------------------------------------------------------- + +TPixelRGBM32 DVAPI TPixelRGBM32::from(const TPixelGR16 &pix) +{ + UCHAR value = byteFromUshort(pix.value); + return TPixelRGBM32(value, value, value, maxChannelValue); +} + +TPixelD DVAPI TPixelD::from(const TPixelCM8 &pix) +{ + double v = (double)pix.value * (1.0 / 255.0); + return TPixelD(v, v, v, v); +} + +TPixelD DVAPI TPixelD::from(const TPixelGR8 &pix) +{ + double v = (double)pix.value * (1.0 / 255.0); + return TPixelD(v, v, v, v); +} + +TPixelD DVAPI TPixelD::from(const TPixelRGBM32 &pix) +{ + const double k = 1.0 / 255.0; + return TPixelD(k * pix.r, k * pix.g, k * pix.b, k * pix.m); +} + +TPixelD DVAPI TPixelD::from(const TPixelRGBM64 &pix) +{ + const double k = 1.0 / 65535.0; + return TPixelD(k * pix.r, k * pix.g, k * pix.b, k * pix.m); +} + +TPixelD DVAPI TPixelD::from(const TPixelGR16 &pix) +{ + double v = (double)pix.value * (1.0 / 65535.0); + return TPixelD(v, v, v, v); +} + +TPixel32 DVAPI TPixel32::from(const TPixelD &pix) +{ + const int max = 255; + return TPixel32( + tcrop((int)(pix.r * max + .5), 0, max), + tcrop((int)(pix.g * max + .5), 0, max), + tcrop((int)(pix.b * max + .5), 0, max), + tcrop((int)(pix.m * max + .5), 0, max)); +} + +TPixel64 DVAPI TPixel64::from(const TPixelD &pix) +{ + const int max = 65535; + return TPixel64( + tcrop((int)(pix.r * max + .5), 0, max), + tcrop((int)(pix.g * max + .5), 0, max), + tcrop((int)(pix.b * max + .5), 0, max), + tcrop((int)(pix.m * max + .5), 0, max)); +} + +//TPixelGR8 TPixelGR8::from(const TPixelD &pix) +//{ +// return from(TPixel32::from(pix)); +//} + +TPixelGR8 DVAPI TPixelGR8::from(const TPixel32 &pix) +{ + return TPixelGR8((((UINT)(pix.r) * 19594 + + (UINT)(pix.g) * 38472 + + (UINT)(pix.b) * 7470 + + (UINT)(1 << 15)) >> + 16)); +} + +//----------------------------------------------------------------------------- + +TPixelGR16 DVAPI TPixelGR16::from(const TPixel64 &pix) +{ + return TPixelGR16((((UINT)(pix.r) * 19594 + + (UINT)(pix.g) * 38472 + + (UINT)(pix.b) * 7470 + + (UINT)(1 << 15)) >> + 16)); +} + +//----------------------------------------------------------------------------- +// class ostream; + +DVAPI ostream &operator<<(ostream &out, const TPixel32 &pixel); +DVAPI ostream &operator<<(ostream &out, const TPixel64 &pixel); +DVAPI ostream &operator<<(ostream &out, const TPixelD &pixel); +DVAPI ostream &operator<<(ostream &out, const TPixelCM8 &pixel); + +/* + +TPixel64 inline TPixel32::to64() const +{ + return TPixel64(ushortFromByte(r), ushortFromByte(g), ushortFromByte(b),ushortFromByte(m)); +}; + +TPixel32 inline TPixel64::to32() const +{ + return TPixel32(byteFromUshort(r), byteFromUshort(g), byteFromUshort(b), byteFromUshort(m)); +}; +*/ + +//----------------------------------------------------------------------------- +#endif diff --git a/toonz/sources/common/tcolor/tspectrum.cpp b/toonz/sources/common/tcolor/tspectrum.cpp new file mode 100644 index 0000000..e6a6152 --- /dev/null +++ b/toonz/sources/common/tcolor/tspectrum.cpp @@ -0,0 +1,20 @@ + + +#include "tspectrum.h" +//#include "tutil.h" +//#include "tpixelutils.h" + +//------------------------------------------------------------------- + +DVAPI TSpectrumT convert(const TSpectrumT &s) +{ + std::vector keys; + for (int i = 0; i < s.getKeyCount(); i++) { + TSpectrumT::Key key = s.getKey(i); + TSpectrum64::ColorKey key64( + key.first, + toPixel64(key.second)); + keys.push_back(key64); + } + return TSpectrumT(keys.size(), &keys[0]); +} diff --git a/toonz/sources/common/tcontenthistory.cpp b/toonz/sources/common/tcontenthistory.cpp new file mode 100644 index 0000000..6ae89fc --- /dev/null +++ b/toonz/sources/common/tcontenthistory.cpp @@ -0,0 +1,313 @@ + + +#include "tcontenthistory.h" +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include +/* +class HistoryRec +{ +QDateTime m_date; + +HistoryRec(const QDateTime&date QString&user, const QString&machine) + : date(date) + , m_user +}; +*/ + +using namespace std; + +//------------------------------------------------------- + +TContentHistory::TContentHistory(bool isLevel) + : m_isLevel(isLevel), m_frozenHistory() +{ +} + +//------------------------------------------------------- + +TContentHistory::~TContentHistory() +{ +} + +//------------------------------------------------------- + +TContentHistory *TContentHistory::clone() const +{ + TContentHistory *history = new TContentHistory(m_isLevel); + history->deserialize(serialize()); + return history; +} + +//------------------------------------------------------- + +void TContentHistory::deserialize(const QString ¤tHistory) +{ + m_frozenHistory = currentHistory; +} + +/* +QStringList strlist = str.split("#"); + +//skip strlist[0], is the header +int i; +for (i=1; i= count) + res.truncate(count); + else + res += QString(count - str.size(), ' '); + return res; +} + +//-------------------------------------------------------------------- + +inline QString getStr(const TFrameId &id) +{ + if (id.getLetter() != 0) + return QString::number(id.getNumber()) + id.getLetter(); + else + return QString::number(id.getNumber()); +} + +//-------------------------------------------------------------------- + +const QString Fmt = "dd MMM yy hh:mm"; + +QString getLine(int counter, const QDateTime &date, const set &frames) +{ + static QString user; + static QString machine; + if (user == "") { + QStringList list = QProcess::systemEnvironment(); + int j; + for (j = 0; j < list.size(); j++) { + QString value = list.at(j); + if (value.startsWith("USERNAME=")) + user = blanks(value.right(value.size() - 9)); + else if (value.startsWith("COMPUTERNAME=")) + machine = blanks(value.right(value.size() - 13)); + } + } + + if (frames.empty()) + return "| #" + blanks(QString::number(counter), 4) + blanks(date.toString(Fmt), 20) + blanks(machine, 12) + user + " |"; + + QString framesStr; + + set::const_iterator it = frames.begin(); + TFrameId first, last; + while (it != frames.end()) { + first = last = *it; + ++it; + while (it != frames.end() && + ((*it).getNumber() == last.getNumber() || // 1a, 1b.... + (*it).getNumber() == last.getNumber() + 1)) { + assert(*it > last); + last = *it, ++it; + } + framesStr += getStr(first) + ((first != last) ? "-" + getStr(last) : ""); + if (it != frames.end()) + framesStr += ", "; + } + + return "| #" + blanks(QString::number(counter), 4) + blanks(date.toString(Fmt), 20) + blanks(machine, 12) + user + blanks(framesStr, 20) + " |"; +} + +//-------------------------------------------------------------------- + +int getCurrentCount(const QString &str) +{ + if (str == "") + return 0; + + int from = str.lastIndexOf('#') + 1; + assert(from != -1); + + int to = str.indexOf(" ", from) - 1; + assert(to != -1); + assert(from <= to); + + return (str.mid(from, to - from + 1)).toInt(); +} + +//-------------------------------------------------------------------- + +const QString TContentHistory::currentToString() const +{ + if (m_records.empty()) + return ""; + + int counter = getCurrentCount(m_frozenHistory); + + if (!m_isLevel) { + assert(m_records.size() == 1); + return getLine(++counter, m_records.begin()->second, set()); + } + + QString out; + std::multimap dateSorted; + std::map::const_iterator it; + for (it = m_records.begin(); it != m_records.end(); ++it) + dateSorted.insert(pair(it->second, it->first)); + + std::multimap::const_iterator it1 = dateSorted.begin(); + QDateTime currDate = it1->first; + + while (it1 != dateSorted.end()) { + set frames; + while (it1 != dateSorted.end() && currDate == it1->first) { + frames.insert(it1->second); + ++it1; + } + assert(!frames.empty()); + out += getLine(++counter, currDate, frames); + if (it1 != dateSorted.end()) + currDate = it1->first; + } + + return out; +} + +//-------------------------------------------------------------------- + +const QString TContentHistory::serialize() const +{ + const QString currentHistory = currentToString(); + + if (m_frozenHistory != "") + return m_frozenHistory + currentHistory; + else if (currentHistory != "") { + if (m_isLevel) + return "| # DATE: Time: MACHINE: USER: FRAMES MODIFIED: |" + currentHistory; + else + return "| # DATE: Time: MACHINE: USER: |" + currentHistory; + } else + return ""; +} + +//-------------------------------------------------------------------- + +void TContentHistory::fixCurrentHistory() +{ + m_frozenHistory = serialize(); + m_records.clear(); +} + +//-------------------------------------------------------------------- + +void TContentHistory::frameRangeModifiedNow(const TFrameId &fromId, const TFrameId &toId) +{ + assert(m_isLevel); + QDateTime date = QDateTime::currentDateTime(); + QDateTime dateNoSecs(date.date(), date.time().addSecs(-date.time().second())); + assert(dateNoSecs.secsTo(date) == date.time().second()); + + int i; + m_records[fromId] = dateNoSecs; + + if (fromId == toId) + return; + + for (i = fromId.getNumber() + 1; i <= toId.getNumber() - 1; i++) + m_records[TFrameId(i)] = dateNoSecs; + + m_records[toId] = dateNoSecs; +} + +//-------------------------------------------------------------------- + +void TContentHistory::modifiedNow() +{ + assert(!m_isLevel); + QDateTime date = QDateTime::currentDateTime(); + QDateTime dateNoSecs(date.date(), date.time().addSecs(-date.time().second())); + assert(dateNoSecs.secsTo(date) == date.time().second()); + + m_records[0] = dateNoSecs; +} + +/* +void testHistory() +{ + { + TContentHistory ch(true); + + ch.frameModifiedNow(TFrameId(13)); + Sleep(1000); + + ch.frameModifiedNow(TFrameId(3, 'c')); + ch.frameRangeModifiedNow(TFrameId(5), TFrameId(7)); + ch.fixCurrentHistory(); + Sleep(2000); + ch.frameRangeModifiedNow(TFrameId(6), TFrameId(9)); + ch.frameModifiedNow(TFrameId(11)); + QString str1 = ch.serialize(); + + TContentHistory ch1(true); + ch1.deserialize(str1); + ch1.frameRangeModifiedNow(TFrameId(2), TFrameId(3, 'c')); + Sleep(2000); + ch1.frameModifiedNow(TFrameId(3, 'a')); + ch1.fixCurrentHistory(); + Sleep(2000); + ch1.frameModifiedNow(TFrameId(11, 'b')); + ch1.frameModifiedNow(TFrameId(12)); + QString str2 = ch1.serialize(); + + QFile f("C:\\temp\\out.txt"); + f.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&f); + out << str2; + } + + + { + TContentHistory ch(false); + + ch.modifiedNow(); + Sleep(1000); + + ch.modifiedNow(); + ch.modifiedNow(); + ch.fixCurrentHistory(); + Sleep(2000); + ch.modifiedNow(); + ch.modifiedNow(); + QString str1 = ch.serialize(); + + TContentHistory ch1(false); + ch1.deserialize(str1); + ch1.modifiedNow(); + Sleep(2000); + ch1.modifiedNow(); + ch1.fixCurrentHistory(); + Sleep(2000); + ch1.modifiedNow(); + ch1.modifiedNow(); + QString str2 = ch1.serialize(); + + QFile f("C:\\temp\\out1.txt"); + f.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&f); + out << str2; + } +} +*/ diff --git a/toonz/sources/common/tcore/tdata.cpp b/toonz/sources/common/tcore/tdata.cpp new file mode 100644 index 0000000..6484742 --- /dev/null +++ b/toonz/sources/common/tcore/tdata.cpp @@ -0,0 +1,27 @@ + + +#include "tdata.h" +#include "tconvert.h" + +DEFINE_CLASS_CODE(TData, 16) + +TTextData::TTextData(string text) + : m_text(toWideString(text)) +{ +} + +TDataP TTextData::clone() const +{ + return new TTextData(m_text); +} + +TDataP TFilePathListData::clone() const +{ + return new TFilePathListData(m_filePaths); +} + +TFilePath TFilePathListData::getFilePath(int i) const +{ + assert(0 <= i && i < (int)m_filePaths.size()); + return m_filePaths[i]; +} diff --git a/toonz/sources/common/tcore/tdebugmessage.cpp b/toonz/sources/common/tcore/tdebugmessage.cpp new file mode 100644 index 0000000..5ff57c9 --- /dev/null +++ b/toonz/sources/common/tcore/tdebugmessage.cpp @@ -0,0 +1,33 @@ + + +#include "tdebugmessage.h" +#include + +using namespace std; + +namespace +{ + +TDebugMessage::Manager *debugManagerInstance = 0; +} + +void TDebugMessage::setManager(Manager *manager) +{ + debugManagerInstance = manager; +} + +ostream &TDebugMessage::getStream() +{ + if (debugManagerInstance) + return debugManagerInstance->getStream(); + else + return cout; +} + +void TDebugMessage::flush(int code) +{ + if (debugManagerInstance) + debugManagerInstance->flush(code); + else + cout << endl; +} diff --git a/toonz/sources/common/tcore/texception.cpp b/toonz/sources/common/tcore/texception.cpp new file mode 100644 index 0000000..52e2ef2 --- /dev/null +++ b/toonz/sources/common/tcore/texception.cpp @@ -0,0 +1,15 @@ + + +#include "texception.h" +#include "tconvert.h" + +TException::TException(const string &msg) +{ + m_msg = toWideString(msg); +} +/* +ostream& operator<<(ostream &out, const TException &e) +{ + return out< + +#include "tfunctorinvoker.h" + +//******************************************************************************** +// TFunctorInvoker definition +//******************************************************************************** + +TFunctorInvoker *TFunctorInvoker::instance() +{ + static TFunctorInvoker theInstance; + return &theInstance; +} + +//----------------------------------------------------------------- + +void TFunctorInvoker::invokeQueued(BaseFunctor *functor) +{ + QMetaObject::invokeMethod( + this, "invoke", Qt::QueuedConnection, Q_ARG(void *, functor)); +} diff --git a/toonz/sources/common/tcore/threadmessage.cpp b/toonz/sources/common/tcore/threadmessage.cpp new file mode 100644 index 0000000..aeb7bf3 --- /dev/null +++ b/toonz/sources/common/tcore/threadmessage.cpp @@ -0,0 +1,91 @@ + + +#include "tthreadmessage.h" +#include "tthreadp.h" +#include + +QThread *MainThread = QThread::currentThread(); + +TThreadMessageDispatcher *Dispatcher; //MUST BE CREATED in the main thread!!!!!! + +//------------------------------------------------------------------------------ + +bool TThread::isMainThread() +{ + return MainThread == QThread::currentThread(); +} + +//------------------------------------------------------------------------------ + +TThreadMessageDispatcher::TThreadMessageDispatcher() +{ + connect(this, SIGNAL(signaled(TThread::Message *)), this, SLOT(onSignal(TThread::Message *))); + connect(this, SIGNAL(blockingSignaled(TThread::Message *)), this, SLOT(onSignal(TThread::Message *)), Qt::BlockingQueuedConnection); +} + +//------------------------------------------------------------------------------ + +void TThreadMessageDispatcher::init() +{ + if (!TThread::isMainThread()) + return; + if (Dispatcher == 0) + Dispatcher = new TThreadMessageDispatcher(); +} + +//------------------------------------------------------------------------------ + +TThreadMessageDispatcher *TThreadMessageDispatcher::instance() +{ + assert(Dispatcher); + return Dispatcher; +} + +//------------------------------------------------------------------------------ + +void TThreadMessageDispatcher::emitSignaled(TThread::Message *msg) +{ + Q_EMIT signaled(msg); +} + +//------------------------------------------------------------------------------ + +void TThreadMessageDispatcher::emitBlockingSignaled(TThread::Message *msg) +{ + Q_EMIT blockingSignaled(msg); +} + +//------------------------------------------------------------------------------ + +void TThreadMessageDispatcher::onSignal(TThread::Message *msg) +{ + msg->onDeliver(); + delete msg; +} + +//------------------------------------------------------------------------------ + +TThread::Message::Message() +{ +} + +//------------------------------------------------------------------------------ +void TThread::Message::send() +{ + if (isMainThread()) + onDeliver(); + else + TThreadMessageDispatcher::instance()->emitSignaled(clone()); +} + +//------------------------------------------------------------------------------ + +void TThread::Message::sendBlocking() +{ + if (isMainThread()) + onDeliver(); + else + TThreadMessageDispatcher::instance()->emitBlockingSignaled(clone()); +} + +//------------------------------------------------------------------------------ diff --git a/toonz/sources/common/tcore/tidentifiable.cpp b/toonz/sources/common/tcore/tidentifiable.cpp new file mode 100644 index 0000000..6737e54 --- /dev/null +++ b/toonz/sources/common/tcore/tidentifiable.cpp @@ -0,0 +1,116 @@ + + +#include "tidentifiable.h" +#include + +namespace +{ + +class IdentifierTable +{ // singleton + + unsigned long m_lastId; + std::map m_table; + std::set m_objects; + + IdentifierTable() : m_lastId(0) {} + +public: + static IdentifierTable *instance() + { + // NON DEVE MORIRE + //static IdentifierTable _instance; + //return &_instance; + static IdentifierTable *_instance = 0; + if (!_instance) + _instance = new IdentifierTable; + return _instance; + } + + unsigned long getNextId() + { + return ++m_lastId; + } + + void insert(TIdentifiable *o) + { + + unsigned long id = o->getIdentifier(); + std::map::iterator it = m_table.find(id); + if (it != m_table.end()) { + if (it->second == o) + return; + m_objects.erase(it->second); + it->second = o; + } else { + m_table[id] = o; + } + m_objects.insert(o); + } + + void erase(TIdentifiable *o) + { + + unsigned long id = o->getIdentifier(); + m_table.erase(id); + m_objects.erase(o); + } + + TIdentifiable *fetch(unsigned long id) + { + std::map::iterator it = m_table.find(id); + return it == m_table.end() ? 0 : it->second; + } +}; + +} // namespace + +TIdentifiable::TIdentifiable() + : m_id(0) +{ +} + +TIdentifiable::~TIdentifiable() +{ + if (m_id != 0) + IdentifierTable::instance()->erase(this); +} + +TIdentifiable::TIdentifiable(const TIdentifiable &src) + : m_id(src.m_id) +{ +} + +const TIdentifiable &TIdentifiable::operator=(const TIdentifiable &src) +{ + if (src.m_id != m_id && m_id != 0) + IdentifierTable::instance()->erase(this); + m_id = src.m_id; + return *this; +} + +void TIdentifiable::setIdentifier(unsigned long id) +{ + bool wasStored = m_id > 0 && IdentifierTable::instance()->fetch(m_id) == this; + if (m_id != id && m_id != 0) + IdentifierTable::instance()->erase(this); + m_id = id; + if (wasStored) + IdentifierTable::instance()->insert(this); +} + +void TIdentifiable::setNewIdentifier() +{ + setIdentifier(IdentifierTable::instance()->getNextId()); +} + +void TIdentifiable::storeByIdentifier() +{ + assert(getIdentifier() >= 1); + IdentifierTable::instance()->insert(this); +} + +TIdentifiable *TIdentifiable::fetchByIdentifier(unsigned long id) +{ + return IdentifierTable::instance()->fetch(id); +} diff --git a/toonz/sources/common/tcore/tmathutil.cpp b/toonz/sources/common/tcore/tmathutil.cpp new file mode 100644 index 0000000..b3ada9f --- /dev/null +++ b/toonz/sources/common/tcore/tmathutil.cpp @@ -0,0 +1,923 @@ + + +#include "tutil.h" +#include "tmathutil.h" +#include "tconvert.h" +#include + +using TConsts::epsilon; + +TMathException::TMathException(string msg) + : m_msg(toWideString(msg)) +{ +} + +namespace +{ + +//------------------------------------------------------------------------- + +//! maximum order for a polynomial +const int MAX_ORDER = 12; + +//! smallest relative error we want +const double RELERROR = 1.0e-14; + +//! max power of 10 we wish to search to +const int MAXPOW = 32; + +//!max number of iterations +const int MAXIT = 800; + +//! a coefficient smaller than SMALL_ENOUGH is considered to be zero (0.0). +const double SMALL_ENOUGH = 1.0e-12; + +//------------------------------------------------------------------------- + +inline int getEl(int i, int j, int n) +{ + return (j - 1) + (i - 1) * n; +} +//------------------------------------------------------------------------- + +//! structure type for representing a polynomial +typedef struct + { + int ord; + double coef[MAX_ORDER]; +} poly; + +//------------------------------------------------------------------------- + +// compute number of root in (min,max) +double evalpoly(int ord, double *coef, double x); +int numchanges(int np, poly *sseq, double a); +int buildsturm(int ord, poly *sseq); +int modrf(int ord, double *coef, double a, double b, double *val); + +//------------------------------------------------------------------------- + +void convert(const vector &v, poly &p) +{ + assert((int)v.size() <= MAX_ORDER); + if ((int)v.size() > MAX_ORDER) + return; + + p.ord = v.size() - 1; + + std::copy(v.begin(), v.end(), p.coef); +} +//------------------------------------------------------------------------- + +void convert(const poly &p, vector &v) +{ + v.resize(p.ord); + std::copy(p.coef, p.coef + p.ord, v.begin()); +} + +//------------------------------------------------------------------------- + +/* + * modp + * + * calculates the modulus of u(x) / v(x) leaving it in r, it + * returns 0 if r(x) is a constant. + * note: this function assumes the leading coefficient of v + * is 1 or -1 + */ +int modp(poly *u, poly *v, poly *r) +{ + int k, j; + double *nr, *end, *uc; + + nr = r->coef; + end = &u->coef[u->ord]; + + uc = u->coef; + while (uc <= end) + *nr++ = *uc++; + + if (v->coef[v->ord] < 0.0) { + for (k = u->ord - v->ord - 1; k >= 0; k -= 2) + r->coef[k] = -r->coef[k]; + + for (k = u->ord - v->ord; k >= 0; k--) + for (j = v->ord + k - 1; j >= k; j--) + r->coef[j] = -r->coef[j] - r->coef[v->ord + k] * v->coef[j - k]; + } else { + for (k = u->ord - v->ord; k >= 0; k--) + for (j = v->ord + k - 1; j >= k; j--) + r->coef[j] -= r->coef[v->ord + k] * v->coef[j - k]; + } + + k = v->ord - 1; + while (k >= 0 && fabs(r->coef[k]) < SMALL_ENOUGH) { + r->coef[k] = 0.0; + k--; + } + + r->ord = (k < 0) ? 0 : k; + + return (r->ord); +} + +//------------------------------------------------------------------------- + +/* + * buildsturm + * + * build up a sturm sequence for a polynomial in sseq, returning + * the number of polynomials in the sequence + */ +int buildsturm(int ord, poly *sseq) +{ + int i; + double f, *fp, *fc; + poly *sp; + + sseq[0].ord = ord; + sseq[1].ord = ord - 1; + + // calculate the derivative and normalise the leadingcoefficient. + f = fabs(sseq[0].coef[ord] * ord); + fp = sseq[1].coef; + fc = sseq[0].coef + 1; + for (i = 1; i <= ord; i++) + *fp++ = *fc++ * i / f; + + // construct the rest of the Sturm sequence + for (sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++) { + // reverse the sign and normalise + f = -fabs(sp->coef[sp->ord]); + for (fp = &sp->coef[sp->ord]; fp >= sp->coef; fp--) + *fp /= f; + } + + sp->coef[0] = -sp->coef[0]; // reverse the sign + + return (sp - sseq); +} + +//------------------------------------------------------------------------- + +/* + return the number of distinct real roots of the polynomial + described in sseq + */ +int numroots(int np, poly *sseq, int &atneg, int &atpos) +{ + int + atposinf = 0, + atneginf = 0; + + poly *s; + double f, lf; + + // change at positive infinity + lf = sseq[0].coef[sseq[0].ord]; + + for (s = sseq + 1; s <= sseq + np; ++s) { + f = s->coef[s->ord]; + if (0.0 == lf || lf * f < 0.0) + ++atposinf; + lf = f; + } + + // change at negative infinity + if (sseq[0].ord & 1) + lf = -sseq[0].coef[sseq[0].ord]; + else + lf = sseq[0].coef[sseq[0].ord]; + + for (s = sseq + 1; s <= sseq + np; ++s) { + if (s->ord & 1) + f = -s->coef[s->ord]; + else + f = s->coef[s->ord]; + + if (0.0 == lf || lf * f < 0.0) + ++atneginf; + lf = f; + } + + atneg = atneginf; + atpos = atposinf; + + return atneginf - atposinf; +} + +//------------------------------------------------------------------------- + +/* + * numchanges + * + * return the number of sign changes in the Sturm sequence in + * sseq at the value a. + */ +int numchanges(int np, poly *sseq, double a) +{ + int changes; + double f, lf; + poly *s; + + changes = 0; + + lf = evalpoly(sseq[0].ord, sseq[0].coef, a); + + for (s = sseq + 1; s <= sseq + np; s++) { + f = evalpoly(s->ord, s->coef, a); + if (lf == 0.0 || lf * f < 0) + changes++; + lf = f; + } + + return (changes); +} + +//------------------------------------------------------------------------- + +/* + * sbisect + * + * uses a bisection based on the sturm sequence for the polynomial + * described in sseq to isolate intervals in which roots occur, + * the roots are returned in the roots array in order of magnitude. + */ +void sbisect(int np, + poly *sseq, + double min, + double max, + int atmin, + int atmax, + double *roots) +{ + double mid; + int n1 = 0, n2 = 0, its, atmid; + + if ((atmin - atmax) == 1) { + //first try a less expensive technique. + + if (modrf(sseq->ord, sseq->coef, min, max, &roots[0])) + return; + + /* + * if we get here we have to evaluate the root the hard + * way by using the Sturm sequence. + */ + for (its = 0; its < MAXIT; its++) { + mid = (min + max) / 2; + + atmid = numchanges(np, sseq, mid); + + if (fabs(mid) > RELERROR) { + if (fabs((max - min) / mid) < RELERROR) { + roots[0] = mid; + return; + } + } else if (fabs(max - min) < RELERROR) { + roots[0] = mid; + return; + } + + if ((atmin - atmid) == 0) + min = mid; + else + max = mid; + } + + if (its == MAXIT) { + /* + fprintf(stderr, "sbisect: overflow min %f max %f\ + diff %e nroot %d n1 %d n2 %d\n", + min, max, max - min, nroot, n1, n2); + */ + roots[0] = mid; + } + + return; + } + + /* + * more than one root in the interval, we have to bisect... + */ + for (its = 0; its < MAXIT; its++) { + mid = (min + max) / 2; + + atmid = numchanges(np, sseq, mid); + + n1 = atmin - atmid; + n2 = atmid - atmax; + + if (n1 != 0 && n2 != 0) { + sbisect(np, sseq, min, mid, atmin, atmid, roots); + sbisect(np, sseq, mid, max, atmid, atmax, &roots[n1]); + break; + } + + if (n1 == 0) + min = mid; + else + max = mid; + } + + if (its == MAXIT) { + /* + fprintf(stderr, "sbisect: roots too close together\n"); + fprintf(stderr, "sbisect: overflow min %f max %f diff %e\ + nroot %d n1 %d n2 %d\n", + min, max, max - min, nroot, n1, n2); + */ + for (n1 = atmax; n1 < atmin; n1++) + roots[n1 - atmax] = mid; + } +} + +//------------------------------------------------------------------------- + +/* + * evalpoly + * + * evaluate polynomial defined in coef returning its value. + */ +double evalpoly(int ord, double *coef, double x) +{ + double *fp, f; + + fp = &coef[ord]; + f = *fp; + + for (fp--; fp >= coef; fp--) + f = x * f + *fp; + + return (f); +} + +//------------------------------------------------------------------------- + +/* + * modrf + * + * uses the modified regula-falsi method to evaluate the root + * in interval [a,b] of the polynomial described in coef. The + * root is returned is returned in *val. The routine returns zero + * if it can't converge. + */ +int modrf(int ord, double *coef, double a, double b, double *val) +{ + int its; + double fa, fb, x, fx, lfx; + double *fp, *scoef, *ecoef; + + scoef = coef; + ecoef = &coef[ord]; + + fb = fa = *ecoef; + for (fp = ecoef - 1; fp >= scoef; fp--) { + fa = a * fa + *fp; + fb = b * fb + *fp; + } + + // if there is no sign difference the method won't work + if (fa * fb > 0.0) + return (0); + + if (fabs(fa) < RELERROR) { + *val = a; + return (1); + } + + if (fabs(fb) < RELERROR) { + *val = b; + return (1); + } + + lfx = fa; + + for (its = 0; its < MAXIT; its++) { + x = (fb * a - fa * b) / (fb - fa); + + fx = *ecoef; + for (fp = ecoef - 1; fp >= scoef; fp--) + fx = x * fx + *fp; + + if (fabs(x) > RELERROR) { + if (fabs(fx / x) < RELERROR) { + *val = x; + return (1); + } + } else if (fabs(fx) < RELERROR) { + *val = x; + return (1); + } + + if ((fa * fx) < 0) { + b = x; + fb = fx; + if ((lfx * fx) > 0) + fa /= 2; + } else { + a = x; + fa = fx; + if ((lfx * fx) > 0) + fb /= 2; + } + + lfx = fx; + } + + // fprintf(stderr, "modrf overflow %f %f %f\n", a, b, fx); + + return (0); +} + +//------------------------------------------------------------------------- + +/*! + a x^2 + b x + c + + Remark: + poly[0] = c + poly[1] = b + poly[2] = a + */ +int rootForQuadraticEquation(const vector &v, + vector &sol) +{ + double q, delta; + + /* + if( isAlmostZero(v[2])) + { + if ( isAlmostZero(v[1]) ) + return -1; + + sol.push_back(-v[0]/v[1]); + return 1; + } + */ + + if (isAlmostZero(v[1])) { + q = -v[0] / v[2]; + if (q < 0) + return 0; + else if (isAlmostZero(q)) { + sol.push_back(0); + return 1; + } + + q = sqrt(q); + sol.push_back(-q); + sol.push_back(q); + + return 2; + } + + delta = sq(v[1]) - 4.0 * v[0] * v[2]; + + if (delta < 0.0) + return 0; + + assert(v[2] != 0); + + if (isAlmostZero(delta)) { + sol.push_back(-v[1] / (v[2] * 2.0)); + return 1; + } + + q = -0.5 * (v[1] + tsign(v[1]) * sqrt(delta)); + + assert(q != 0); + + sol.push_back(v[0] / q); + sol.push_back(q / v[2]); + + return 2; +} + +//----------------------------------------------------------------------------- + +/* + a x^3+b x^2 + c x + d + + Remark: + poly[0] = d + poly[1] = c + poly[2] = b + poly[3] = a + */ +int rootForCubicEquation(const vector &p, + vector &sol) +{ + /* + if( isAlmostZero(p[3]) ) + return rootForQuadraticEquation(p,sol); + */ + + if (isAlmostZero(p[0])) { + int numberOfSol; + vector redPol(3); + redPol[0] = p[1]; + redPol[1] = p[2]; + redPol[2] = p[3]; + + numberOfSol = rootForQuadraticEquation(redPol, sol); + + for (int i = 0; i < numberOfSol; ++i) + if (0.0 == sol[i]) + return numberOfSol; + + // altrimenti devo contare la soluzione nulla + ++numberOfSol; + sol.push_back(0); + return numberOfSol; + } + + double + inv_v3 = 1.0 / p[3], + a = p[2] * inv_v3, + b = p[1] * inv_v3, + c = p[0] * inv_v3; + + static const double inv_3 = 1.0 / 3.0; + static const double inv_9 = 1.0 / 9.0; + static const double inv_54 = 1.0 / 54.0; + + double + Q = (sq(a) - 3.0 * b) * inv_9, + R = (2.0 * sq(a) * a - 9.0 * a * b + 27.0 * c) * inv_54; + + double + R_2 = sq(R), + Q_3 = sq(Q) * Q; + + if (R_2 < Q_3) { + double Q_sqrt = sqrt(Q); + double theta = acos(R / (Q * Q_sqrt)); + sol.push_back(-2 * Q_sqrt * cos(theta * inv_3) - a * inv_3); + sol.push_back(-2 * Q_sqrt * cos((theta - TConsts::pi * 2.0) * inv_3) - a * inv_3); + sol.push_back(-2 * Q_sqrt * cos((theta + TConsts::pi * 2.0) * inv_3) - a * inv_3); + std::sort(sol.begin(), sol.end()); + return 3; + } + + double A = -tsign(R) * pow((fabs(R) + sqrt(R_2 - Q_3)), inv_3); + double B = A != 0 ? Q / A : 0; + sol.push_back((A + B) - a * inv_3); + return 1; +} + +//----------------------------------------------------------------------------- + +int rootForGreaterThanThreeEquation(const vector &p, + vector &sol) +{ + poly sseq[MAX_ORDER]; + + convert(p, sseq[0]); + + int np = buildsturm(p.size() - 1, sseq); + + int atmin, atmax; + + int nroot = numroots(np, sseq, atmin, atmax); + + if (nroot == 0) + return 0; + + double minVal = -1.0; + + UINT i = 0; + + int nchanges = numchanges(np, sseq, minVal); + + for (i = 0; nchanges != atmin && i != (UINT)MAXPOW; ++i) { + minVal *= 10.0; + nchanges = numchanges(np, sseq, minVal); + } + + if (nchanges != atmin) { + atmin = nchanges; + } + + double maxVal = 1.0; + nchanges = numchanges(np, sseq, maxVal); + + for (i = 0; nchanges != atmax && i != (UINT)MAXPOW; ++i) { + maxVal *= 10.0; + nchanges = numchanges(np, sseq, maxVal); + } + + if (nchanges != atmax) { + atmax = nchanges; + } + + nroot = atmin - atmax; + + assert(nroot > 0); + + poly outPoly; + + outPoly.ord = nroot; + + sbisect(np, sseq, minVal, maxVal, atmin, atmax, outPoly.coef); + + convert(outPoly, sol); + + return nroot < 0 ? -1 : nroot; +} + +//----------------------------------------------------------------------------- + +} // end of unnamed namespace + +//----------------------------------------------------------------------------- + +void tLUDecomposition(double *a, int n, int *indx, double &d) +{ + int i, imax, j, k; + double big, dum, sum, temp; + std::vector vv(n); + + d = 1.0; + for (i = 1; i <= n; i++) { + big = 0.0; + for (j = 1; j <= n; j++) + if ((temp = fabs(a[getEl(i, j, n)])) > big) + big = temp; + + if (big == 0.0) + throw TMathException("Singular matrix in routine tLUDecomposition\n"); + + vv[i - 1] = 1.0 / big; + } + + for (j = 1; j <= n; j++) { + for (i = 1; i < j; i++) { + sum = a[getEl(i, j, n)]; + for (k = 1; k < i; k++) + sum -= a[getEl(i, k, n)] * a[getEl(k, j, n)]; + a[getEl(i, j, n)] = sum; + } + big = 0.0; + for (i = j; i <= n; i++) { + sum = a[getEl(i, j, n)]; + for (k = 1; k < j; k++) + sum -= a[getEl(i, k, n)] * a[getEl(k, j, n)]; + a[getEl(i, j, n)] = sum; + if ((dum = vv[i - 1] * fabs(sum)) >= big) { + big = dum; + imax = i; + } + } + + if (j != imax) { + for (k = 1; k <= n; k++) { + dum = a[getEl(imax, k, n)]; + a[getEl(imax, k, n)] = a[getEl(j, k, n)]; + a[getEl(j, k, n)] = dum; + } + d = -(d); + vv[imax - 1] = vv[j - 1]; + } + + indx[j - 1] = imax; + + if (a[getEl(j, j, n)] == 0.0) + a[getEl(j, j, n)] = TConsts::epsilon; + + if (j != n) { + dum = 1.0 / (a[getEl(j, j, n)]); + for (i = j + 1; i <= n; i++) + a[getEl(i, j, n)] *= dum; + } + } +} + +//----------------------------------------------------------------------------- + +void tbackSubstitution(double *a, int n, int *indx, double *b) +{ + int i, ii = 0, ip, j; + double sum; + + for (i = 1; i <= n; i++) { + ip = indx[i - 1]; + sum = b[ip - 1]; + b[ip - 1] = b[i - 1]; + if (ii) + for (j = ii; j <= i - 1; j++) + sum -= a[getEl(i, j, n)] * b[j - 1]; + else if (sum) + ii = i; + b[i - 1] = sum; + } + for (i = n; i >= 1; i--) { + sum = b[i - 1]; + for (j = i + 1; j <= n; j++) + sum -= a[getEl(i, j, n)] * b[j - 1]; + b[i - 1] = sum / a[getEl(i, i, n)]; + } +} + +//----------------------------------------------------------------------------- + +double tdet(double *LUa, int n, double d) +{ + for (int i = 1; i <= n; ++i) + d *= LUa[getEl(i, i, n)]; + + return d; +} + +//----------------------------------------------------------------------------- + +double tdet(double *a, int n) +{ + double d; + vector indx(n); + + tLUDecomposition(a, n, &indx[0], d); + for (int i = 1; i <= n; ++i) + d *= a[getEl(i, i, n)]; + + return d; +} + +//----------------------------------------------------------------------------- + +void tsolveSistem(double *a, int n, double *res) +{ + double d; + vector indx(n); + + tLUDecomposition(a, n, &indx[0], d); + assert(tdet(a, n, d) != 0); + /* + if( isAlmostZero(tdet(a, n, d)) ) + throw TMathException("Singular matrix in routine tLUDecomposition\n"); + */ + tbackSubstitution(a, n, &indx[0], res); +} + +//----------------------------------------------------------------------------- + +int rootFinding(const vector &in_poly, + vector &sol) +{ + // per ora risolvo solo i polinomi di grado al piu' pari a 3 + assert((int)in_poly.size() <= MAX_ORDER); + if (in_poly.empty() || (int)in_poly.size() > MAX_ORDER) + return -1; + + std::vector p; + std::copy(in_poly.begin(), in_poly.end(), std::back_inserter(p)); + + // eat zero in poly + while (!p.empty() && isAlmostZero(p.back())) + p.pop_back(); + + sol.clear(); + + while (!p.empty() && p.front() == 0) { + sol.push_back(0.0); + p.erase(p.begin()); //se i coefficienti bassi sono zero, ci sono soluzioni pari a 0.0. le metto, + } //e abbasso il grado del polinomio(piu' veloce) + + switch (p.size()) { + case 0: + if (sol.empty()) + return INFINITE_SOLUTIONS; + CASE 1 : //no solutions + CASE 2 : sol.push_back(-p[0] / p[1]); + CASE 3 : rootForQuadraticEquation(p, sol); + CASE 4 : rootForCubicEquation(p, sol); + DEFAULT: + rootForGreaterThanThreeEquation(p, sol); + } + return sol.size(); +} + +//----------------------------------------------------------------------------- + +/* +*/ +int numberOfRootsInInterval(int order, const double *polyH, double min, double max) +{ + poly sseq[MAX_ORDER]; + + int + i, + nchanges_0, + nchanges_1, + np; + + if (order > MAX_ORDER) + return -1; + + while (polyH[order] == 0.0 && order > 0) + --order; + + // init a sturm's sequence with our polynomious + for (i = order; i >= 0; --i) + sseq[0].coef[i] = polyH[i]; + + // build the Sturm sequence + np = buildsturm(order, sseq); + + //compute number of variation on 0.0 + nchanges_0 = numchanges(np, sseq, min); + + nchanges_1 = numchanges(np, sseq, max); + + return (nchanges_0 - nchanges_1); +} + +//----------------------------------------------------------------------------- + +double quadraticRoot(double a, double b, double c) +{ + double bb, q; + // caso lineare + if (fabs(a) < epsilon) { + if (fabs(b) >= epsilon) + return -c / b; + return 1; + } + bb = b * b; + q = bb - 4.0 * a * c; + if (q < 0.0) + return 1; + q = sqrt(q); + if (b < 0.0) + q = -q; + q = -0.5 * (b + q); + double root1 = -1; + double root2 = -1; + if (fabs(q) >= epsilon) + root1 = c / q; + if (fabs(a) >= epsilon) + root2 = q / a; + if (0.0 - epsilon <= root1 && root1 <= 1.0 + epsilon) + return root1; + if (0.0 - epsilon <= root2 && root2 <= 1.0 + epsilon) + return root2; + return 1; +} + +//----------------------------------------------------------------------------- + +double cubicRoot(double a, double b, double c, double d) +{ + double A, Q, R, QQQ, RR; + double theta; + /* Test for a quadratic or linear degeneracy */ + if (fabs(a) < epsilon) + return quadraticRoot(b, c, d); + /* Normalize */ + b /= a; + c /= a; + d /= a; + a = 1.0; + /* Compute discriminants */ + Q = (b * b - 3.0 * c) / 9.0; + QQQ = Q * Q * Q; + R = (2.0 * b * b * b - 9.0 * b * c + 27.0 * d) / 54.0; + RR = R * R; + /* Three real roots */ + if (RR < QQQ) { + theta = acos(R / sqrt(QQQ)); + double root[3]; + root[0] = root[1] = root[2] = -2.0 * sqrt(Q); + root[0] *= cos(theta / 3.0); + root[1] *= cos((theta + 2 * TConsts::pi) / 3.0); + root[2] *= cos((theta - 2 * TConsts::pi) / 3.0); + root[0] -= b / 3.0; + root[1] -= b / 3.0; + root[2] -= b / 3.0; + if (0.0 - epsilon < root[0] && root[0] < 1.0 + epsilon) + return root[0]; + if (0.0 - epsilon < root[1] && root[1] < 1.0 + epsilon) + return root[1]; + if (0.0 - epsilon < root[2] && root[2] < 1.0 + epsilon) + return root[2]; + return 1; + } + /* One real root */ + else { + double root = 0; + A = -pow(fabs(R) + sqrt(RR - QQQ), 1.0 / 3.0); + if (A != 0.0) { + if (R < 0.0) + A = -A; + root = A + Q / A; + } + root -= b / 3.0; + if (0.0 - epsilon < root && root < 1.0 + epsilon) + return root; + return 1; + } +} + +//----------------------------------------------------------------------------- +// End Of File +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tcore/trandom.cpp b/toonz/sources/common/tcore/trandom.cpp new file mode 100644 index 0000000..a408f88 --- /dev/null +++ b/toonz/sources/common/tcore/trandom.cpp @@ -0,0 +1,209 @@ + + +#include "trandom.h" + +TRandom::RANDOM_FLOAT_TYPE TRandom::RandomFloatType = TRandom::RANDOM_FLOAT_TYPE_NONE; + +TRandom::TRandom(UINT _seed) + : seed(_seed) +{ + if (RandomFloatType == TRandom::RANDOM_FLOAT_TYPE_NONE) + setRandomFloatType(); + reset(); +} + +//-------------------------------------------------------------------------- + +TRandom::~TRandom() +{ +} + +//-------------------------------------------------------------------------- + +void TRandom::setSeed(UINT s) +{ + seed = s; + reset(); +} +//-------------------------------------------------------------------------- + +void TRandom::reset() +{ + UINT uj, uk; + int i, ii, k; + + assert(sizeof(UINT) == 32 / 8); + + uj = 161803398 - seed; + ran[55] = uj; + uk = 1; + for (i = 1; i <= 54; i++) { + ii = (21 * i) % 55; + ran[ii] = uk; + uk = uj - uk; + uj = ran[ii]; + } + for (k = 1; k <= 4; k++) + for (i = 1; i <= 55; i++) + ran[i] -= ran[1 + (i + 30) % 55]; + idx1 = 55; + idx2 = 31; +} + +//-------------------------------------------------------------------------- + +inline void TRandom::setRandomFloatType() +{ + UINT u; + + assert(sizeof(float) == sizeof(UINT)); + u = 0x3f800000; + if ((*(float *)&u) == 1.0F) + RandomFloatType = RANDOM_FLOAT_TYPE_1; + else { + u = 0x0000803f; + if ((*(float *)&u) == 1.0F) + RandomFloatType = RANDOM_FLOAT_TYPE_2; + else + assert(0); + } +} + +//-------------------------------------------------------------------------- + +inline UINT TRandom::getNextUINT() +{ + idx1++; + if (idx1 == 56) + idx1 = 1; + idx2++; + if (idx2 == 56) + idx2 = 1; + ran[idx1] = ran[idx1] - ran[idx2]; + return ran[idx1]; +} + +//-------------------------------------------------------------------------- + +UINT TRandom::getUInt(UINT end) // [0,end[ +{ + if (end == 0) + return 0; + UINT u = getNextUINT(); + if (end == c_maxuint) + return u; + + return u % end; +} + +//-------------------------------------------------------------------------- + +int TRandom::getInt(int begin, int end) // [begin, end[ +{ + assert(end >= begin); + int limit = end - begin; + if (limit == 0) // end == begin + return begin; + UINT u = getNextUINT(); + return u % limit + begin; +} + +//-------------------------------------------------------------------------- + +float TRandom::getFloat() // [0,1[ +{ + UINT u = getNextUINT(); + + switch (RandomFloatType) { + case RANDOM_FLOAT_TYPE_1: + u = (u >> 5) & 0x007fffff | 0x3f800000; + break; + case RANDOM_FLOAT_TYPE_2: + u = u & 0xffff7f00 | 0x0000803f; + break; + default: + assert(0); + u = 0; + break; + } + return (*(float *)&u) - 1.0F; +} + +//-------------------------------------------------------------------------- + +float TRandom::getFloat(float end) // [0, end[ +{ + return getFloat() * end; +} + +//-------------------------------------------------------------------------- + +float TRandom::getFloat(float begin, float end) // [begin, end[ +{ + assert(end >= begin); + return (getFloat() * (end - begin)) + begin; +} + +//-------------------------------------------------------------------------- + +bool TRandom::getBool() +{ + UINT u = getNextUINT(); + return u & 1; +} + +//-------------------------------------------------------------------------- + +double TRandom::getDouble() // [0,1[ +{ + return getFloat(); +#ifdef DA_RIVEDERE_O_PROPRIO_IMPLEMENTARE + UINT low = getNextUINT(); + UINT high = getNextUINT(); + double value = 0; + + void *ptr = &value; + UINT *retL = (UINT *)ptr; + UINT *retH = retL + 1; + +/* +// questa parte e' stata commentata perche' su tutte le piattaforme il tipo +//di float e' RANDOM_FLOAT_TYPE_1ma su irix c'e' bisogno di eseguire le +//istruzioni sotto RANDOM_FLOAT_TYPE_2 +switch (RandomFloatType) + { + case RANDOM_FLOAT_TYPE_1: + *retH = high; + *retH &= 0x00efffff; + *retH |= 0x3f000000; + + *retL = low ; +// vecchi commenti +// *retH = high; +// *retL = (low >> 5) & 0x007fffff | 0x3ff00000; + + return value; + break; + case RANDOM_FLOAT_TYPE_2: *retH = low; + *retL = (high >> 5) & 0x007fffff | 0x3ff00000; + break; + //!!!!!occhio andrebbe sopra il break + return value-1.0; + default: assert (0); + } +return -1; +*/ +#ifndef __sgi + *retH = high; + *retH &= 0x007fffff; + *retH |= 0x3ff00000; + *retL = low; + return value - 1.0; +#else + *retH = low; + *retL = (high >> 5) & 0x007fffff | 0x3ff00000; + return value - 1.0; +#endif + +#endif +} diff --git a/toonz/sources/common/tcore/tsmartpointer.cpp b/toonz/sources/common/tcore/tsmartpointer.cpp new file mode 100644 index 0000000..f3cab2a --- /dev/null +++ b/toonz/sources/common/tcore/tsmartpointer.cpp @@ -0,0 +1,84 @@ + + +#include "tsmartpointer.h" +#include "tthreadmessage.h" + +//------------------------------------------------------------------- + +namespace +{ + +//------------------------------------------------------------------- + +typedef TAtomicVar *TAtomicVarPtr; + +const int maxClassCode = 200; +TAtomicVarPtr instanceCounts[maxClassCode + 1]; + +//------------------------------------------------------------------- + +inline TAtomicVar &getInstanceCounter(TINT32 classCode) +{ + assert(0 <= classCode && classCode <= maxClassCode); + TAtomicVarPtr &instanceCountPtr = instanceCounts[classCode]; + if (instanceCountPtr == 0) { + static TThread::Mutex mutex; + TThread::MutexLocker g(&mutex); + if (instanceCountPtr == 0) + instanceCountPtr = new TAtomicVar(); + } + assert(instanceCountPtr); + return *instanceCountPtr; +} + +//------------------------------------------------------------------- + +} // namespace + +//------------------------------------------------------------------- + +#ifdef INSTANCE_COUNT_ENABLED +const TINT32 TSmartObject::m_unknownClassCode = 0; +#endif + +void TSmartObject::incrementInstanceCount() +{ +#ifdef INSTANCE_COUNT_ENABLED + TAtomicVar &instanceCount = getInstanceCounter(m_classCodeRef); + ++instanceCount; +#else + assert(0); +#endif +} + +//------------------------------------------------------------------- + +void TSmartObject::decrementInstanceCount() +{ +#ifdef INSTANCE_COUNT_ENABLED + TAtomicVar &instanceCount = getInstanceCounter(m_classCodeRef); + assert(instanceCount > 0); + --instanceCount; +#else + assert(0); +#endif +} + +//------------------------------------------------------------------- + +TINT32 TSmartObject::getInstanceCount(ClassCode +#ifdef INSTANCE_COUNT_ENABLED + code +#endif + ) +{ +#ifdef INSTANCE_COUNT_ENABLED + TAtomicVar &instanceCount = getInstanceCounter(code); + return instanceCount; +#else + assert(0); + return 0; +#endif +} + +//------------------------------------------------------------------- diff --git a/toonz/sources/common/tcore/tstopwatch.cpp b/toonz/sources/common/tcore/tstopwatch.cpp new file mode 100644 index 0000000..dd0cb16 --- /dev/null +++ b/toonz/sources/common/tcore/tstopwatch.cpp @@ -0,0 +1,435 @@ + + +#include "tstopwatch.h" + +#ifdef _WIN32 +#include +#else //_WIN32 + +#if defined(__APPLE_CC__) +#include +#else +#include +#endif +#include +#include +#include +#include + +#ifndef STW_TICKS_PER_SECOND +#ifndef WIN32 +extern "C" long sysconf(int); +#define STW_TICKS_PER_SECOND sysconf(_SC_CLK_TCK) +#else +#define STW_TICKS_PER_SECOND CLK_TCK +#endif +#endif +#endif + +#define MAXSWNAMELENGHT 40 +#define MAXSWTIMELENGHT 12 + +TStopWatch TStopWatch::StopWatch[10]; + +enum TimerType { TTUUnknown, + TTUHiRes, + TTUTickCount }; +static void determineTimer(); + +#ifdef WIN32 + +static TimerType timerToUse = TTUUnknown; + +static LARGE_INTEGER perfFreq; // ticks per second +static int perfFreqAdjust = 0; // in case Freq is too big +static int overheadTicks = 0; // overhead in calling timer + +#else +static TimerType timerToUse = TTUTickCount; +#endif + +using namespace std; + +//----------------------------------------------------------- + +TStopWatch::TStopWatch(string name) + : m_name(name), m_active(false), m_isRunning(false) +{ + if (timerToUse == TTUUnknown) + determineTimer(); + + m_start = 0; +#ifdef _WIN32 + m_startUser.dwHighDateTime = m_startUser.dwLowDateTime = 0; + m_startSystem.dwHighDateTime = m_startSystem.dwLowDateTime = 0; +#else + m_startUser = 0; + m_startSystem = 0; +#endif //_WIN32 + m_tm = 0; + m_tmUser = 0; + m_tmSystem = 0; +} + +//----------------------------------------------------------- + +TStopWatch::~TStopWatch() +{ + m_active = false; +} + +//----------------------------------------------------------- + +void TStopWatch::setStartToCurrentTime() +{ +#ifdef _WIN32 + FILETIME creationTime, exitTime; + BOOL ret = GetProcessTimes(GetCurrentProcess(), // specifies the process of interest + &creationTime, + &exitTime, + &m_startSystem, + &m_startUser); + + if (timerToUse == TTUTickCount) { + m_start = GetTickCount(); + } else { + QueryPerformanceCounter(&m_hrStart); + } +#else + struct tms clk; + m_start = times(&clk); + m_startUser = clk.tms_utime; + m_startSystem = clk.tms_stime; +#endif //_WIN32 +} + +//----------------------------------------------------------- + +void TStopWatch::reset() +{ + m_tm = 0; + m_tmUser = 0; + m_tmSystem = 0; + setStartToCurrentTime(); +} + +//----------------------------------------------------------- + +void TStopWatch::start(bool resetFlag) +{ + if (resetFlag) + reset(); + if (m_isRunning) + return; + m_active = true; + m_isRunning = true; + setStartToCurrentTime(); +} + +//----------------------------------------------------------- + +#ifdef _WIN32 +inline __int64 FileTimeToInt64(LPFILETIME pFileTime) +{ + __int64 val; + val = pFileTime->dwHighDateTime; + val <<= 32; + val |= pFileTime->dwLowDateTime; + return val; +} +#endif //_WIN32 + +//----------------------------------------------------------- + +// +// Aggiunge il tempo trascorso fra start(startUser, startSystem) e l'istante corrente +// a tm(tmUser, tmSystem) +// +static void checkTime(START start, START_USER startUser, START_SYSTEM startSystem, + TM_TOTAL &tm, TM_USER &tmUser, TM_SYSTEM &tmSystem) +{ + assert(timerToUse == TTUTickCount); + +#ifdef WIN32 + + DWORD tm_stop; + FILETIME creationTime, exitTime, stopSystem, stopUser; + BOOL ret = GetProcessTimes(GetCurrentProcess(), // specifies the process of interest + &creationTime, + &exitTime, + &stopSystem, + &stopUser); + tm_stop = GetTickCount(); + assert(tm_stop >= start); + tm += tm_stop - start; //total elapsed time + + tmUser += FileTimeToInt64(&stopUser) - FileTimeToInt64(&startUser); //user elapsed time + tmSystem += FileTimeToInt64(&stopSystem) - FileTimeToInt64(&startSystem); //system elapsed time + +#else //WIN32 + + struct tms clk; + clock_t tm_stop; + tm_stop = times(&clk); + assert(tm_stop >= start); + tm += tm_stop - start; + tmUser += clk.tms_utime - startUser; + tmSystem += clk.tms_stime - startSystem; + +#endif //WIN32 +} + +//----------------------------------------------------------- + +#ifdef WIN32 + +// +// come checkTime, ma usa i timer ad alta risoluzione +// +namespace +{ + +//----------------------------------------------------------- + +void hrCheckTime(LARGE_INTEGER start, START_USER startUser, START_SYSTEM startSystem, + TM_TOTAL &tm, TM_USER &tmUser, TM_SYSTEM &tmSystem) +{ + assert(timerToUse != TTUTickCount); + + LARGE_INTEGER hrTm_stop; + FILETIME creationTime, exitTime, stopSystem, stopUser; + BOOL ret = GetProcessTimes(GetCurrentProcess(), // specifies the process of interest + &creationTime, + &exitTime, + &stopSystem, + &stopUser); + + QueryPerformanceCounter(&hrTm_stop); + assert(hrTm_stop.HighPart > start.HighPart || + hrTm_stop.HighPart == start.HighPart && hrTm_stop.LowPart >= start.LowPart); + + LARGE_INTEGER Freq = perfFreq; + int Oht = overheadTicks; + + LARGE_INTEGER dtime; + //faccio "a mano" la differenza dtime = m_tStop - m_tStart + dtime.HighPart = hrTm_stop.HighPart - start.HighPart; + if (hrTm_stop.LowPart >= start.LowPart) + dtime.LowPart = hrTm_stop.LowPart - start.LowPart; + else { + assert(dtime.HighPart > 0); + dtime.HighPart--; + dtime.LowPart = hrTm_stop.LowPart + ~start.LowPart + 1; + } + + int shift = 0; + if (Freq.HighPart > 0) { + int h = Freq.HighPart; + while (h > 0) { + h >>= 1; + shift++; + } + } + if ((dtime.HighPart >> shift) > 0) { + int h = dtime.HighPart >> shift; + while (h > 0) { + h >>= 1; + shift++; + } + } + if (shift > 0) { + dtime.QuadPart = Int64ShrlMod32(dtime.QuadPart, shift); + Freq.QuadPart = Int64ShrlMod32(Freq.QuadPart, shift); + } + assert(Freq.HighPart == 0); + assert(dtime.HighPart == 0); + + double totalTime = 1000.0 * dtime.LowPart / Freq.LowPart; + tm += troundp(totalTime); + + tmUser += FileTimeToInt64(&stopUser) - FileTimeToInt64(&startUser); //user elapsed time + tmSystem += FileTimeToInt64(&stopSystem) - FileTimeToInt64(&startSystem); //system elapsed time +} + +//----------------------------------------------------------- + +} //namespace + +#endif //WIN32 + +//----------------------------------------------------------- + +void TStopWatch::stop() +{ + if (!m_isRunning) + return; + m_isRunning = false; +#ifdef WIN32 + if (timerToUse == TTUTickCount) + checkTime(m_start, m_startUser, m_startSystem, m_tm, m_tmUser, m_tmSystem); + else + hrCheckTime(m_hrStart, m_startUser, m_startSystem, m_tm, m_tmUser, m_tmSystem); +#else + checkTime(m_start, m_startUser, m_startSystem, m_tm, m_tmUser, m_tmSystem); +#endif +} + +//----------------------------------------------------------- + +void TStopWatch::getElapsedTime(TM_TOTAL &tm, TM_USER &user, TM_SYSTEM &system) +{ + if (m_isRunning) { + TM_TOTAL cur_tm = 0; + TM_USER cur_tmUser = 0; + TM_SYSTEM cur_tmSystem = 0; + +#ifdef WIN32 + if (timerToUse == TTUTickCount) + checkTime(m_start, m_startUser, m_startSystem, cur_tm, cur_tmUser, cur_tmSystem); + else + hrCheckTime(m_hrStart, m_startUser, m_startSystem, cur_tm, cur_tmUser, cur_tmSystem); +#else + checkTime(m_start, m_startUser, m_startSystem, cur_tm, cur_tmUser, cur_tmSystem); +#endif + + tm = m_tm + cur_tm; + user = m_tmUser + cur_tmUser; + system = m_tmSystem + cur_tmSystem; + } else { + tm = m_tm; + user = m_tmUser; + system = m_tmSystem; + } +} + +//----------------------------------------------------------- + +TUINT32 TStopWatch::getTotalTime() +{ + TM_TOTAL tm; + TM_USER user; + TM_SYSTEM system; + getElapsedTime(tm, user, system); +#ifdef _WIN32 + return tm; +#else + return (TINT32)(tm * 1000) / STW_TICKS_PER_SECOND; +#endif //_WIN32 +} + +//----------------------------------------------------------- + +TUINT32 TStopWatch::getUserTime() +{ + TM_TOTAL tm; + TM_USER user; + TM_SYSTEM system; + getElapsedTime(tm, user, system); +#ifdef _WIN32 + return (TINT32)(user / 10000); +#else + return (TINT32)(user * 1000) / STW_TICKS_PER_SECOND; +#endif //_WIN32 +} + +//----------------------------------------------------------- + +TUINT32 TStopWatch::getSystemTime() +{ + TM_TOTAL tm; + TM_USER user; + TM_SYSTEM system; + getElapsedTime(tm, user, system); +#ifdef _WIN32 + return (TINT32)(system / 10000); +#else + return (TINT32)(system * 1000) / STW_TICKS_PER_SECOND; +#endif //_WIN32 +} + +//----------------------------------------------------------- + +TStopWatch::operator string() +{ + char buffer[256]; + ostrstream out(buffer, sizeof(buffer)); + out << m_name.c_str() << ": " << (int)getTotalTime() << " u" << (int)getUserTime() << " s" << (TINT32)getSystemTime(); + return string(buffer, out.pcount()); +} + +//------------------------------------------------------------ + +void TStopWatch::print() +{ + print(cout); +} + +//------------------------------------------------------------------------------------------- + +void TStopWatch::print(ostream &out) +{ + string s(*this); + out << s.c_str() << endl; +} + +//------------------------------------------------------------------------------------------- + +void TStopWatch::printGlobals(ostream &out) +{ + const int n = sizeof(StopWatch) / sizeof(StopWatch[0]); + for (int i = 0; i < n; i++) + if (StopWatch[i].m_active) + StopWatch[i].print(out); +} + +//------------------------------------------------------------------------------------------- + +void TStopWatch::printGlobals() +{ + printGlobals(cout); +} + +//----------------------------------------------------------- +#ifdef WIN32 + +void dummyFunction() +{ + // It's used just to calculate the overhead + return; +} + +void determineTimer() +{ + + void (*pFunc)() = dummyFunction; + // cout << "DETERMINE TIMER" << endl; + // Assume the worst + timerToUse = TTUTickCount; + if (QueryPerformanceFrequency(&perfFreq)) { + // We can use hires timer, determine overhead + timerToUse = TTUHiRes; + overheadTicks = 200; + for (int i = 0; i < 20; i++) { + LARGE_INTEGER b, e; + int Ticks; + QueryPerformanceCounter(&b); + (*pFunc)(); + QueryPerformanceCounter(&e); + Ticks = e.LowPart - b.LowPart; + if (Ticks >= 0 && Ticks < overheadTicks) + overheadTicks = Ticks; + } + // See if Freq fits in 32 bits; if not lose some precision + perfFreqAdjust = 0; + int High32 = perfFreq.HighPart; + while (High32) { + High32 >>= 1; + perfFreqAdjust++; + } + } +} +#else +void determineTimer() +{ +} +#endif diff --git a/toonz/sources/common/tcore/tstring.cpp b/toonz/sources/common/tcore/tstring.cpp new file mode 100644 index 0000000..05a8d14 --- /dev/null +++ b/toonz/sources/common/tcore/tstring.cpp @@ -0,0 +1,257 @@ + + +#include "tconvert.h" +//#include "texception.h" +#include "tfilepath.h" + +#ifndef TNZCORE_LIGHT +#include +#endif + +#ifdef WIN32 +#pragma warning(disable : 4996) +#include "windows.h" +#endif + +class TStringConvertException : public TException +{ + string m_string; + +public: + TStringConvertException(const string str) : m_string(str) {} +}; + +wstring toWideString(string s) +{ +#ifdef TNZCORE_LIGHT + std::wstring ws; + ws.assign(s.begin(), s.end()); + return ws; +#else + + QString testString = QString::fromStdString(s); + QString qString = QString::fromUtf8(s.c_str()); + + // To detect if 's' is UTF-8 encoded or not + if (qString != testString && string(qString.toUtf8()) == s) + return qString.toStdWString(); + else + return testString.toStdWString(); +#endif +} + +string toString(wstring ws) +{ +#ifdef TNZCORE_LIGHT + std::string s; + s.assign(ws.begin(), ws.end()); + return s; +#else + + QString qString = QString::fromStdWString(ws); + +// Test if 'ws' is not unicode (UTF-8) +#if 0 + if(qString.toAscii() == qString) +#else + if (qString.toLatin1() == qString) +#endif + return qString.toStdString(); + + QByteArray a = qString.toUtf8(); + return string(a); +#endif +} + +string toString(const TFilePath &fp) +{ + return toString(fp.getWideString()); +} + +wstring toWideString(int x) +{ + return toWideString(toString(x)); +} + +string toString(int value) +{ + ostrstream ss; + ss << value << '\0'; + string s = ss.str(); + ss.freeze(false); + return s; +} + +string toString(unsigned long value) +{ + ostrstream ss; + ss << value << '\0'; + string s = ss.str(); + ss.freeze(false); + return s; +} + +string toString(unsigned long long value) +{ + ostrstream ss; + ss << value << '\0'; + string s = ss.str(); + ss.freeze(false); + return s; +} + +/*! + The default precision is six decimal places. If the + precision is less than of the decimal places in the fractonal + part, the remainder is not cut off but rounded. +*/ + +string toString(double value, int prec) +{ + ostrstream ss; + ss.setf(std::ios_base::fixed, std::ios_base::floatfield); + if (prec >= 0) + ss.precision(prec); + ss << value << '\0'; + string s = ss.str(); + ss.freeze(0); + return s; +} + +string toString(void *p) +{ + ostrstream ss; + ss << p << '\0'; + string s = ss.str(); + ss.freeze(false); + return s; +} + +int toInt(string str) +{ + int value = 0; + for (int i = 0; i < (int)str.size(); i++) + value = value * 10 + str[i] - '0'; + return value; +} + +int toInt(wstring str) +{ + return toInt(toString(str)); +} + +bool isInt(string s) +{ + int i = 0, len = (int)s.size(); + if (len == 0) + return false; + if (s[0] == '-') { + if (len == 1) + return false; + else + i++; + } + + while (i < len) { + if (s[i] < '0' || s[i] > '9') + return false; + i++; + } + return true; +} + +bool isDouble(string s) +{ + int i = 0, len = (int)s.size(); + if (len == 0) + return false; + if (i < len && s[i] == '-') + i++; + while (i < len && s[i] != '.') { + if (s[i] < '0' || s[i] > '9') + return false; + i++; + } + if (i >= len) + return true; + i++; + while (i < len) { + if (s[i] < '0' || s[i] > '9') + return false; + i++; + } + return true; +} + +bool isInt(wstring s) { return isInt(toString(s)); } +bool isDouble(wstring s) { return isDouble(toString(s)); } + +double toDouble(string str) +{ + double value; + istrstream ss(str.c_str(), (std::streamsize)str.length()); + ss >> value; + return value; +} + +double toDouble(wstring str) +{ + return toDouble(toString(str)); +} + +wstring toWideString(double v, int p) +{ + return toWideString(toString(v, p)); +} + +string toUpper(string a) +{ +#ifdef WIN32 + return _strupr(const_cast(a.c_str())); +#else + string ret = a; + for (int i = 0; i < (int)ret.length(); i++) + ret[i] = toupper(ret[i]); + return ret; +#endif +} + +string toLower(string a) +{ +#ifdef WIN32 + return _strlwr(const_cast(a.c_str())); +#else + string ret = a; + for (int i = 0; i < (int)ret.length(); i++) + ret[i] = tolower(ret[i]); + return ret; +#endif +} + +wstring toUpper(wstring a) +{ +#ifdef WIN32 + return _wcsupr(const_cast(a.c_str())); +#else + wstring ret; + for (int i = 0; i < (int)a.length(); i++) { + wchar_t c = towupper(a[i]); + ret += c; + } + return ret; +#endif +} + +wstring toLower(wstring a) +{ +#ifdef WIN32 + return _wcslwr(const_cast(a.c_str())); +#else + wstring ret; + for (int i = 0; i < (int)a.length(); i++) { + wchar_t c = towlower(a[i]); + ret += c; + } + return ret; +#endif +} diff --git a/toonz/sources/common/tcore/tthread.cpp b/toonz/sources/common/tcore/tthread.cpp new file mode 100644 index 0000000..a487b77 --- /dev/null +++ b/toonz/sources/common/tcore/tthread.cpp @@ -0,0 +1,1133 @@ + + +#ifdef _DEBUG +#define _STLP_DEBUG 1 +#endif + +// TnzCore includes +#include "tthreadmessage.h" +#include "tsystem.h" +#include "tatomicvar.h" + +#include "tthread.h" +#include "tthreadP.h" + +// STL includes +#include +#include + +// tcg includes +#include "tcg/tcg_pool.h" + +//Qt includes +#include +#include +#include +#include +#include +#include + +//============================================================================== + +//================================================== +// Paradigms of the Executor tasks management +//-------------------------------------------------- + +//Basics: +// * Tasks added by Executors are always stored in a global QMultiMap first - +// ordering primarily being the schedulingPriority(), and insertion instant +// (implicit) when they have the same scheduling priority. +// * The QMultiMap needs to be reverse-iterated to do that, since values with +// the same key are ordered from the most recent to the oldest one (see Qt's +// manual). +// * Worker threads are stored in a global set. +// * When a task is added or a task has been performed, the workers list is +// refreshed, possibly adding new Workers for some executable tasks. +// * When a worker ends a task, it automatically takes a new one before refreshing +// the workers list. If no task can be taken, by default the thread exits and +// invokes its own destruction. +// * The thread may instead be put to rest if explicitly told by the user with +// the appropriate method. + +//Default execution conditions: +// * A task is executable if, by default, its task load added to the sum of that of +// all other active tasks does not exceed the available resources of the system +// (i.e.: 100 * # of cores of the machine). +// In other words, in every instant of execution, the sum of all active task's loads +// never exceeds the available machine resources. +// * When such default execution condition is not met when attempting to take the +// task, no other task is taken instead - we wait until enough resources have been +// freed before attempting to take the same task again. +// In other words, the default execution condition is *BLOCKING*. + +//Custom execution conditions: +// * The user may decide to impose more tight conditions for tasks added by a certain +// Executor. Let's call such conditions 'custom' conditions. +// * Custom conditions are always tested *AFTER* the default ones (on the same task). +// This is necessary to enforce the global scheduling priorities mechanism. +// * When no task of a certain executor is active, custom conditions are always considered +// satisfied. +// * If custom conditions are not met, we enqueue the task in an Executor-private queue +// for later execution and remove it from the global tasks queue - making it possible +// to perform other tasks before our custom-failed one (such operation will be called +// "ACCUMULATION"). +// In other words, custom conditions are *NOT BLOCKING* among different Executors. +// * Tasks in the last task's Executor-private queue are always polled before those in +// the global queue by Workers which ended a task, and this is done in a *BLOCKING* way +// inside the same Executor. +// * When an Executor-private tasks queue is not empty, all the other tasks added by the +// same executor are scheduled in the queue. +// In other words, the order of execution is always that of global insertion, *inside +// the same executor*. +// * Tasks polled from an Executor-private queue (which therefore satisfied custom conditions) +// may still fail with default execution conditions. If that happens, we put the task +// back into the global queue with highest possible priority (timedOut) and the worker dies. +// Tasks with this special priority are polled *before every other task*. So, again, default +// conditions are *BLOCKING*. + +//Thread-safety: +// * Most of the following code is mutex-protected, altough it might seem not - indeed, +// only *one* mutex is locked and unlocked all of the time. This 'transition mutex' is +// the key to thread-safety: we're considered to lie in a 'transition state' if we are +// operating outside the run() of some task - which covers almost the totality of the code. +// * The transition mutex is *not* recursive. The reason is that threads con not wait on +// QWaitConditions if the mutex is recursive. That makes it necessary (and welcome) to put +// mutex lockers in strategic points of the code - in many low-level functions no mutex +// is locked *because some caller already did it before*. If you're modifying the code +// always trace back the callers of a function before inserting misleading mutex lockers. + +//============================================================================== + +//================== +// TODO list +//------------------ + +// * Improve dedicated threads support: make sure that tasks added to a dedicated +// executor are directly moved to the accumulation queue. The setDedicatedThreads() +// method must therefore react accordingly. + +// * It could be possible to implement a dependency-based mechanism... + +// * Should the hosting thread wait for worker ones upon ExecutorImp destruction?? +// It could be a problem on some forever-waiting tasks... + +// * Ricontrolla che con le ultime modifiche gli ExecutorId rimangano quando si passano +// i puntatori in takeTask e refreshAss.. + +//============================================================================== + +using namespace TThread; + +DEFINE_CLASS_CODE(TThread::Runnable, 21) + +//============================================================================== + +//========================================== +// Global init() initializer function +//------------------------------------------ + +void TThread::init() +{ + Executor::init(); + TThreadMessageDispatcher::init(); +} + +//--------------------------------------------------------------------- + +void TThread::shutdown() +{ + Executor::shutdown(); +} + +//============================================================================== + +namespace TThread +{ + +//============================================================================== + +//=========================== +// Worker Thread class +//--------------------------- + +//! A Worker is a specialized QThread that continuously polls Runnable +//! tasks from a global execution queue to make them work. +class Worker : public QThread +{ +public: + RunnableP m_task; + + TSmartPointerT m_master; + + bool m_exit; + QWaitCondition m_waitCondition; + + Worker(); + ~Worker(); + + void run(); + + inline void takeTask(); + inline bool canAdopt(const RunnableP &task); + inline void adoptTask(RunnableP &task); + + inline void rest(); + + inline void updateCountsOnTake(); + inline void updateCountsOnRelease(); + + inline void onFinish(); +}; + +//===================================================================== + +//======================== +// ExecutorId class +//------------------------ + +//! Contains all the executor data that need to persist until all tasks +//! added by the executor have been processed. Upon creation of an Executor +//! object, it instantiates one ExecutorId for both storing the Executor's data +//! sensible to the underlying code of the Executor manager, and to identify all +//! tasks added through it - by copying the smart pointer to the id into each +//! added task. +//! \sa Executor and Runnable class. +class ExecutorId : public TSmartObject +{ +public: + size_t m_id; + + int m_activeTasks; + int m_maxActiveTasks; + + int m_activeLoad; + int m_maxActiveLoad; + + bool m_dedicatedThreads; + bool m_persistentThreads; + std::deque m_sleepings; + + ExecutorId(); + ~ExecutorId(); + + inline void accumulate(const RunnableP &task); + + void newWorker(RunnableP &task); + void refreshDedicatedList(); +}; + +//===================================================================== + +//=========================== +// Executor::Imp class +//--------------------------- + +//! ExecutorImp both manages the allocation of worker threads for the +//! execution of Runnable tasks and centralizes the tasks collection. +//! One process only hosts one instance of the ExecutorImp class as a +//! a global variable that needs to be allocated in an application-lasting +//! and event-looped thread - typically the main thread in GUI applications. +class ExecutorImp +{ +public: + QMultiMap m_tasks; + std::set m_workers; // Used just for debugging purposes + + tcg::indices_pool<> m_executorIdPool; + std::vector m_waitingFlagsPool; + + int m_activeLoad; + int m_maxLoad; + + QMutex m_transitionMutex; // Workers' transition mutex + + ExecutorImp(); + ~ExecutorImp(); + + inline void insertTask(int schedulingPriority, RunnableP &task); + + void refreshAssignments(); + + inline bool isExecutable(RunnableP &task); +}; + +//===================================================================== + +} // namespace TThread + +//===================================================================== + +//=========================== +// Global variables +//--------------------------- + +namespace +{ +ExecutorImp *globalImp = 0; +ExecutorImpSlots *globalImpSlots = 0; +bool shutdownVar = false; +} + +//===================================================================== + +//============================= +// ExecutorImp methods +//----------------------------- + +ExecutorImp::ExecutorImp() + : m_activeLoad(0), m_maxLoad(TSystem::getProcessorCount() * 100), m_transitionMutex() //NOTE: We'll wait on this mutex - so it can't be recursive +{ +} + +//--------------------------------------------------------------------- + +ExecutorImp::~ExecutorImp() +{ +} + +//--------------------------------------------------------------------- + +//A task is executable <==> its load allows it. The task load is considered +//fixed until another isExecutable() call is made again - in case it may +//change in time. +inline bool ExecutorImp::isExecutable(RunnableP &task) +{ + return m_activeLoad + task->m_load <= m_maxLoad; +} + +//--------------------------------------------------------------------- + +inline void ExecutorImp::insertTask(int schedulingPriority, RunnableP &task) +{ + task->m_schedulingPriority = schedulingPriority; + m_tasks.insert(schedulingPriority, task); +} + +//===================================================================== + +//======================== +// Runnable methods +//------------------------ + +Runnable::Runnable() + : TSmartObject(m_classCode), m_id(0) +{ +} + +//--------------------------------------------------------------------- + +Runnable::~Runnable() +{ + if (m_id) + m_id->release(); //see Executor::addTask() +} + +//--------------------------------------------------------------------- + +//! Returns the predicted CPU load generated by the task, expressed in percentage +//! of core usage (that is, 100 is intended to fully occupy one processing core). +//! Appropriate task load calibration is an important step to take when implementing +//! a new task; for this purpose, remember some rules to follow: +//!
    +//!
  • In every moment, the task manager ensures that the overall sum of the active +//! task's load does not exceed the number of machine's processing cores multiplied +//! by 100. This condition is \a blocking with respect to the execution of any other +//! task - meaning that when a task is about to be executed the task manager \a waits +//! until enough CPU resources are available to make it run. +//! In particular, observe that a task's load \b never has to exceed the total CPU +//! resources - doing so would surely result in a block of your application. The number +//! of available cores can be accessed via the \b TSystem::getProcessorCount() or +//! \b QThread::idealThreadCount(). + +//!
  • The task load is considered constant for the duration of the task. Changing its +//! value does not affect the task manager in any way once the task has been started. + +//!
  • The default task load is 0, representing a very light task. If the task load +//! is 0 the condition at point 1 always succeeds - so the task is always executed when +//! encountered. Observe that a long succession of 0 task loads leads to the creation of +//! a proportional number of threads simultaneously running to dispatch it; if this is +//! a problem, consider the use of \b Executor::setMaxActiveTasks() +//! to make only a certain number of tasks being executed at the same time. +//!
+int Runnable::taskLoad() +{ + return 0; +} + +//--------------------------------------------------------------------- + +//! Returns the priority value used to schedule a task for execution. Tasks +//! with higher priority start before tasks with lower priority. The default +//! value returned is 5 (halfway from 0 to 10) - but any value other than +//! (std::numeric_limits::max)() is acceptable. +int Runnable::schedulingPriority() +{ + return 5; +} + +//--------------------------------------------------------------------- + +//! Returns the QThread::Priority used by worker threads when they adopt +//! the task. The default value returned is QThread::Normal. +QThread::Priority Runnable::runningPriority() +{ + return QThread::NormalPriority; +} + +//--------------------------------------------------------------------- + +inline bool Runnable::customConditions() +{ + return (m_id->m_activeTasks < m_id->m_maxActiveTasks) && + (m_id->m_activeLoad + m_load <= m_id->m_maxActiveLoad); +} + +/*! + \fn void Runnable::started(RunnableP sender) + + This signal is emitted from working threads just before the run() code + is executed. Observe that the passed smart pointer ensures the survival of + the emitting task for the time required by connected slots execution. + + \warning The started(), finished() and exception() signals are emitted in + a mutex-protected environment in order to provide the correct sequence of + emissions (i.e. so that canceled() and terminated() controller-emitted signals + are either delivered after started() and before finished() or exception(), + or \a instead of them). + \warning Thus, setting up blocking connections or \a direct slots that contain a + blocking instruction or even calls to the Executor API (which would definitely + try to relock the aforementioned mutex) is dangerous and could result in an + application freeze. + \warning In case it's necessary to use blocking features, they should be enforced + through custom signals to be invoked manually in the run() method, outside the + mutex. + + \sa \b finished and \b exception signals. +*/ + +/*! + \fn void Runnable::finished(RunnableP sender) + + The \b finished signal is emitted from working threads once the run() + code is returned without unmanaged exceptions. + + \sa \b started and \b exception signals. +*/ + +/*! + \fn void Runnable::exception(RunnableP sender) + + The \b exception signal is emitted from working threads whenever an + untrapped exception is found within the run() method. + + \sa \b started and \b finished signals. +*/ + +/*! + \fn void Runnable::canceled(RunnableP sender) + + The \b canceled signal is emitted from the controller thread whenever + a task which is currently under the task manager's control is canceled + by the user (the signal is emitted from the thread invoking the cancel). + Observe that tasks under execution are not stopped by the task manager + when they are canceled, but the signal is emitted anyway - helping the + user to stop the actual execution of the run() code in advance. + + \sa \b Executor::removeTask and \b Executor::cancelAll methods. +*/ + +/*! + \fn void Runnable::terminated(RunnableP sender) + + The \b terminated signal is emitted from the controller thread when + the Executor components are shutting down inside a call to + Executor::shutdown(). Implementing a slot connected to this signal + helps the user in controlling the flow of an Executor-multithreaded + application when it is shutting down - for example, it can be imposed + that the application must wait for the task to be finished, print logs + or similar. + This signal is always preceded by a canceled() signal, informing all + active tasks that thay should begin quitting on their own in a 'soft' + way before brute termination may occur. + + \sa \b Executor::shutdown static method and \b Runnable::canceled signal. +*/ + +//! Convenience slot for the started() signal - so it's not necessary to declare +//! the task in a header file for moc'ing each time. You must both reimplement +//! \b and connect it to the started() signal to make it work. +void Runnable::onStarted(RunnableP) +{ +} + +//--------------------------------------------------------------------- + +//! The analogous of onStarted() for the finished() signal. +void Runnable::onFinished(RunnableP) +{ +} + +//--------------------------------------------------------------------- + +//! The analogous of onStarted() for the exception() signal. +void Runnable::onException(RunnableP) +{ +} + +//--------------------------------------------------------------------- + +//! The analogous of onStarted() for the canceled() signal. +void Runnable::onCanceled(RunnableP) +{ +} + +//--------------------------------------------------------------------- + +//! The analogous of onStarted() for the terminated() signal. +void Runnable::onTerminated(RunnableP) +{ +} + +//===================================================================== + +//========================== +// ExecutorId methods +//-------------------------- + +ExecutorId::ExecutorId() + : m_activeTasks(0), m_maxActiveTasks(1), m_activeLoad(0), m_maxActiveLoad((std::numeric_limits::max)()), m_dedicatedThreads(false), m_persistentThreads(false) +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + m_id = globalImp->m_executorIdPool.acquire(); + globalImp->m_waitingFlagsPool.resize(globalImp->m_executorIdPool.size()); +} + +//--------------------------------------------------------------------- + +ExecutorId::~ExecutorId() +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + if (m_dedicatedThreads) { + m_persistentThreads = 0; + refreshDedicatedList(); + } + + globalImp->m_executorIdPool.release(m_id); +} + +//--------------------------------------------------------------------- + +//Make sure that sleeping workers are eliminated properly if the permanent +//workers count decreases. +void ExecutorId::refreshDedicatedList() +{ + //QMutexLocker transitionLocker(&globalImp->m_transitionMutex); //Already covered + + if (!m_dedicatedThreads || !m_persistentThreads) { + //Release all sleeping workers + //Wake them - they will exit on their own + + unsigned int i, size = m_sleepings.size(); + for (i = 0; i < size; ++i) { + m_sleepings[i]->m_exit = true; + m_sleepings[i]->m_waitCondition.wakeOne(); + } + + m_sleepings.clear(); + } +} + +//===================================================================== + +//=========================== +// Worker methods +//--------------------------- + +Worker::Worker() + : QThread(), m_task(0), m_master(0), m_exit(true) +{ +} + +//--------------------------------------------------------------------- + +Worker::~Worker() +{ +} + +//--------------------------------------------------------------------- + +void Worker::run() +{ + //Ensure atomicity of worker's state transitions + QMutexLocker sl(&globalImp->m_transitionMutex); + + if (shutdownVar) + return; + + for (;;) { + //Run the taken task + setPriority(m_task->runningPriority()); + + try { + Q_EMIT m_task->started(m_task); + sl.unlock(); + + m_task->run(); + + sl.relock(); + Q_EMIT m_task->finished(m_task); + } catch (...) { + sl.relock(); //throw must be in the run() block + Q_EMIT m_task->exception(m_task); + } + + updateCountsOnRelease(); + + if (shutdownVar) + return; + + //Get the next task + takeTask(); + + if (!m_task) { + onFinish(); + + if (!m_exit && !shutdownVar) { + //Put the worker to sleep + m_waitCondition.wait(sl.mutex()); + + //Upon thread destruction the wait condition is implicitly woken up. + //If this is the case, m_task == 0 and we return. + if (!m_task || shutdownVar) + return; + } else + return; + } + } +} + +//--------------------------------------------------------------------- + +inline void Worker::updateCountsOnTake() +{ + globalImp->m_activeLoad += m_task->m_load; + m_task->m_id->m_activeLoad += m_task->m_load; + ++m_task->m_id->m_activeTasks; +} + +//--------------------------------------------------------------------- + +inline void Worker::updateCountsOnRelease() +{ + globalImp->m_activeLoad -= m_task->m_load; + m_task->m_id->m_activeLoad -= m_task->m_load; + --m_task->m_id->m_activeTasks; +} + +//--------------------------------------------------------------------- + +inline void Worker::onFinish() +{ + if (m_master && m_master->m_dedicatedThreads && m_master->m_persistentThreads) { + m_exit = false; + m_master->m_sleepings.push_back(this); + + // Unlock the mutex - since eventually invoked ~ExecutorId will relock it... + globalImp->m_transitionMutex.unlock(); + + m_master = 0; //Master may be destroyed here - and m_exit= true for all sleepings + //in that case + + globalImp->m_transitionMutex.lock(); + } else { + m_exit = true; + globalImp->m_workers.erase(this); + } +} + +//===================================================================== + +//=========================== +// Executor methods +//--------------------------- + +Executor::Executor() + : m_id(new ExecutorId) +{ + m_id->addRef(); +} + +//--------------------------------------------------------------------- + +Executor::~Executor() +{ + m_id->release(); +} + +//--------------------------------------------------------------------- + +//! This static method declares the use of the Executor's task manager into +//! the application code. Be sure to use it according to the following rules: +//!
    +//!
  • Only QCoreApplications or QApplications may use Executors. +//!
  • This method must be invoked in a thread which performs constant Qt event +//! processing - like the main loop of interactive GUI applications. +//!
  • No task processing is allowed after event processing stops. +//!
+void Executor::init() +{ + //If no global ExecutorImp exists, allocate it now. You may not move this + //to a static declaration, since ExecutorImpSlots's connections must be + //made once the QCoreApplication has been constructed. + if (!globalImp) { + globalImp = new ExecutorImp; + globalImpSlots = new ExecutorImpSlots; + } + + qRegisterMetaType("TThread::RunnableP"); +} + +//--------------------------------------------------------------------- + +//! This static method, which \b must be invoked in the controller thread, declares +//! termination of all Executor-based components, forcing the execution of tasks submitted +//! by any Executor to quit as soon as possible in a safe way. +//! When the shutdown method is invoked, the task manager first emits a canceled() +//! signal for all the tasks that were submitted to it, independently from the Executor that +//! performed the submission; then, tasks that are still active once all the cancellation signals +//! were delivered further receive a terminated() signal informing them that they must provide +//! code termination (or at least remain silent in a safe state until the application quits). + +//! \b NOTE: Observe that this method does not explicitly wait for all the tasks to terminate - this depends +//! on the code connected to the terminated() signal and is under the user's responsibility (see the +//! remarks specified in started() signal descritpion); if this is the intent and the terminated slot +//! is invoked in the controller thread, you should remember to implement a local event loop in it (so that +//! event processing is still performed) and wait there until the first finished() or catched() +//! slot make it quit. +void Executor::shutdown() +{ + { + //Updating tasks list - lock against state transitions + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + shutdownVar = true; + + //Cancel all tasks - first the active ones + std::set::iterator it; + for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end(); ++it) { + RunnableP task = (*it)->m_task; + if (task) + Q_EMIT task->canceled(task); + } + + //Finally, deal with the global queue tasks + QMutableMapIterator jt(globalImp->m_tasks); + while (jt.hasNext()) { + jt.next(); + RunnableP task = jt.value(); + Q_EMIT task->canceled(task); + jt.remove(); + } + + //Now, send the terminate() signal to all active tasks + for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end(); ++it) { + RunnableP task = (*it)->m_task; + if (task) + Q_EMIT task->terminated(task); + } + } + + //Just placing a convenience processEvents() to make sure that queued slots invoked by the + //signals above are effectively invoked in this method - without having to return to an event loop. + QCoreApplication::processEvents(); +} + +//--------------------------------------------------------------------- + +//! Specifies the use of dedicated threads for the Executor's task group. + +//! By default a worker thread attempts adoption of Runnable tasks +//! without regard to the Executor that performed the submission. This helps +//! in stabilizing the number of threads that are created and destroyed +//! by the task manager - but may be a problem in some cases. + +//! Using this method the user can explicitly tell the Executor to seize the +//! ownership of worker threads assigned to its tasks, so that they will not +//! try adoption of external tasks but instead remain focused on Executor's +//! tasks only. + +//! An optional \b persistent parameter may be passed, which specifies if +//! dedicated threads should remain sleeping or should rather die when no +//! processable tasks from the Executor are found. + +//! This method is especially helpful in two occasions: +//!
    +//!
  • The Executor's tasks use thread-specific data such as QThreadStorages, +//! which may be recycled among different tasks. +//!
  • The Executor receives tasks at a frequent rate, but mostly ends each one before +//! another one is submitted - resulting in a continuous thread turnover. +//!
+ +void Executor::setDedicatedThreads(bool dedicated, bool persistent) +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + m_id->m_dedicatedThreads = dedicated; + m_id->m_persistentThreads = persistent; + + m_id->refreshDedicatedList(); +} + +//--------------------------------------------------------------------- + +//! Submits a task for execution. The task is executed according to +//! its task load, insertion time and scheduling priority. +void Executor::addTask(RunnableP task) +{ + { + if (task->m_id) // Must be done outside transition lock, since eventually + task->m_id->release(); // invoked ~ExecutorId will lock it + + //Updating tasks and workers list - lock against state transitions + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + task->m_id = m_id; + m_id->addRef(); + + globalImp->insertTask(task->schedulingPriority(), task); + } + + //If addTask is called in the main thread, the emit works directly - + //so it is necessary to unlock the mutex *before* emitting the refresh. + globalImpSlots->emitRefreshAssignments(); +} + +//--------------------------------------------------------------------- + +//! Removes the given task from scheduled execution and emits its +//! Runnable::canceled signal. Tasks already under execution are not +//! stopped by this method - although the canceled signal is still emitted. +//! It has no effect if the task is not currently under the task manager's control. +//! \sa \b Runnable::canceled signal and the \b cancelAll method. +void Executor::removeTask(RunnableP task) +{ + //If the task does not belong to this Executor, quit. + if (task->m_id != m_id) + return; + + //Updating tasks list - lock against state transitions + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + //Then, look in the global queue - if it is found, emiminate the task and + //send the canceled signal. + if (globalImp->m_tasks.remove(task->m_schedulingPriority, task)) { + Q_EMIT task->canceled(task); + return; + } + + //Finally, the task may be running - look in workers. + std::set &workers = globalImp->m_workers; + std::set::iterator it; + for (it = workers.begin(); it != workers.end(); ++it) + if (task && (*it)->m_task == task) + Q_EMIT task->canceled(task); + + //No need to refresh - tasks were eventually decremented... +} + +//--------------------------------------------------------------------- + +//! Clears the task manager of all tasks added by this Executor and emits +//! the Runnable::canceled signal for each of them. The same specifications +//! described in the \b removeTask method apply here. +//! \sa \b Runnable::canceled signal and the \b removeTask method. +void Executor::cancelAll() +{ + //Updating tasks list - lock against state transitions + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + //Clear the tasks chronologically. So, first check currently working + //tasks. + std::set::iterator it; + for (it = globalImp->m_workers.begin(); it != globalImp->m_workers.end(); ++it) { + RunnableP task = (*it)->m_task; + if (task && task->m_id == m_id) + Q_EMIT task->canceled(task); + } + + //Finally, clear the global tasks list from all tasks inserted by this executor + //NOTE: An easier way here? + QMutableMapIterator jt(globalImp->m_tasks); + while (jt.hasNext()) { + jt.next(); + if (jt.value()->m_id == m_id) { + RunnableP task = jt.value(); + Q_EMIT task->canceled(task); + jt.remove(); + } + } +} + +//--------------------------------------------------------------------- + +//! Declares that only a certain number of tasks added by this Executor +//! may be processed simultaneously. The default is 1 - meaning that tasks +//! added to the executor are completely serialized. A negative task number +//! disables any form of task serialization. +//! \b NOTE: Currently, tasks that do not +//! satisfy this condition avoid blocking execution of tasks not +//! added by the same Executor - even if they were scheduled for later +//! execution. +void Executor::setMaxActiveTasks(int maxActiveTasks) +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + if (maxActiveTasks <= 0) + m_id->m_maxActiveTasks = (std::numeric_limits::max)(); + else + m_id->m_maxActiveTasks = maxActiveTasks; +} + +//--------------------------------------------------------------------- + +int Executor::maxActiveTasks() const +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + return m_id->m_maxActiveTasks; +} + +//--------------------------------------------------------------------- + +//! Declares a maximum overall task load for the tasks added by this Executor. +//! \b NOTE: The same remark for setMaxActiveTasks() holds here. +void Executor::setMaxActiveLoad(int maxActiveLoad) +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + m_id->m_maxActiveLoad = maxActiveLoad; +} + +//--------------------------------------------------------------------- + +int Executor::maxActiveLoad() const +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + return m_id->m_maxActiveLoad; +} + +//===================================================================== + +//================================== +// ExecutorImpSlots methods +//---------------------------------- + +ExecutorImpSlots::ExecutorImpSlots() +{ + connect(this, SIGNAL(refreshAssignments()), this, SLOT(onRefreshAssignments())); +} + +//--------------------------------------------------------------------- + +ExecutorImpSlots::~ExecutorImpSlots() +{ +} + +//--------------------------------------------------------------------- + +void ExecutorImpSlots::emitRefreshAssignments() +{ + Q_EMIT refreshAssignments(); +} + +//--------------------------------------------------------------------- + +void ExecutorImpSlots::onRefreshAssignments() +{ + QMutexLocker transitionLocker(&globalImp->m_transitionMutex); + + globalImp->refreshAssignments(); +} + +//--------------------------------------------------------------------- + +void ExecutorImpSlots::onTerminated() +{ + delete QObject::sender(); +} + +//===================================================================== +// Task adoption methods +//--------------------------------------------------------------------- + +inline void ExecutorId::newWorker(RunnableP &task) +{ + Worker *worker; + + if (m_sleepings.size()) { + worker = m_sleepings.front(); + m_sleepings.pop_front(); + worker->m_task = task; + worker->updateCountsOnTake(); + worker->m_waitCondition.wakeOne(); + } else { + worker = new Worker; + globalImp->m_workers.insert(worker); + QObject::connect(worker, SIGNAL(finished()), globalImpSlots, SLOT(onTerminated())); + worker->m_task = task; + worker->updateCountsOnTake(); + worker->start(); + } +} + +//--------------------------------------------------------------------- + +inline void Worker::adoptTask(RunnableP &task) +{ + m_task = task; + updateCountsOnTake(); +} + +//--------------------------------------------------------------------- + +// * Le task timedOut sono ex-accumulate che non soddisfano le condizioni +// standard. Quindi sono bloccanti *ovunque*. + +// * Il refresh dei worker sulle task accumulate *deve* avvenire: +// -Se e solo se una task dello stesso executor finisce, +// perche' e' l'unico caso in cui le custom conditions +// vengono aggiornate + +// * Se un thread dedicato non puo' prendere una task estranea, non e' detto +// che altri non possano! +// * Le task che richiedono dedizione dovrebbero essere adottate da thread +// gia' dedicati, se esistono! => Gli executor che richiedono thread dedicati +// li creano a parte e non li condividono con nessuno: cioe', thread creati +// senza dedizione non devono poter adottare task richiedenti dedizione! + +//--------------------------------------------------------------------- + +//Assigns tasks polled from the id's accumulation queue (if id is given) and +//the global tasks queue. +//It works like: + +// a) First look if there exist tasks with timedOut priority and if so +// try to take them out +// b) Then look for tasks in the id's accumulation queue +// c) Finally search in the remaining global tasks queue + +void ExecutorImp::refreshAssignments() +{ + //QMutexLocker transitionLocker(&globalImp->m_transitionMutex); //Already covered + + if (m_tasks.isEmpty()) + return; + + // Erase the id vector data + assert(m_executorIdPool.size() == m_waitingFlagsPool.size()); + memset(&m_waitingFlagsPool.front(), 0, m_waitingFlagsPool.size()); + + //c) Try with the global queue + int e, executorsCount = m_executorIdPool.acquiredSize(); + + int i, tasksCount = m_tasks.size(); + QMultiMap::iterator it; + for (i = 0, e = 0, it = m_tasks.end() - 1; i < tasksCount && e < executorsCount; ++i, --it) { + //std::cout<< "global tasks-refreshAss" << std::endl; + //Take the task + RunnableP task = it.value(); + task->m_load = task->taskLoad(); + + UCHAR &idWaitingForAnotherTask = m_waitingFlagsPool[task->m_id->m_id]; + if (idWaitingForAnotherTask) + continue; + + if (!isExecutable(task)) + break; + + if (!task->customConditions()) { + ++e; + idWaitingForAnotherTask = 1; + } else { + task->m_id->newWorker(task); + it = m_tasks.erase(it); + } + } +} + +//--------------------------------------------------------------------- + +inline bool Worker::canAdopt(const RunnableP &task) +{ + return task->m_id->m_sleepings.size() == 0 && //Always prefer sleeping dedicateds if present + (!m_master || (m_master.getPointer() == task->m_id)); //If was seized by an Executor, ensure task compatibility +} + +//--------------------------------------------------------------------- + +//Takes a task and assigns it to the worker in a way similar to the one above. +inline void Worker::takeTask() +{ + TSmartPointerT oldId = m_task->m_id; + + //When a new task is taken, the old one's Executor may seize the worker + m_master = oldId->m_dedicatedThreads ? oldId : (TSmartPointerT)0; + + //c) No accumulated task can be taken - look for a task in the global tasks queue. + // If the active load admits it, take the earliest task. + + //Free the old task. NOTE: This instruction MUST be performed OUTSIDE the mutex-protected environment - + //since user code may be executed upon task destruction - including the mutex relock!! + + globalImp->m_transitionMutex.unlock(); + + m_task = 0; + oldId = TSmartPointerT(); + + globalImp->m_transitionMutex.lock(); + + // Erase the executor id status pool + tcg::indices_pool<> &executorIdPool = globalImp->m_executorIdPool; + std::vector &waitingFlagsPool = globalImp->m_waitingFlagsPool; + + assert(waitingFlagsPool.size() == globalImp->m_executorIdPool.size()); + memset(&waitingFlagsPool.front(), 0, waitingFlagsPool.size()); + + int e, executorsCount = executorIdPool.acquiredSize(); + + int i, tasksCount = globalImp->m_tasks.size(); + QMultiMap::iterator it; + for (i = 0, e = 0, it = globalImp->m_tasks.end() - 1; i < tasksCount && e < executorsCount; ++i, --it) { + //std::cout<< "global tasks-takeTask" << std::endl; + //Take the first task + RunnableP task = it.value(); + task->m_load = task->taskLoad(); + + UCHAR &idWaitingForAnotherTask = waitingFlagsPool[task->m_id->m_id]; + if (idWaitingForAnotherTask) + continue; + + if (!globalImp->isExecutable(task)) + break; + + //In case the worker was captured for dedication, check the task compatibility. + if (!canAdopt(task)) { + //some other worker may still take the task... + globalImpSlots->emitRefreshAssignments(); + break; + } + + //Test its custom conditions + if (!task->customConditions()) { + ++e; + idWaitingForAnotherTask = 1; + } else { + adoptTask(task); + it = globalImp->m_tasks.erase(it); + + globalImpSlots->emitRefreshAssignments(); + break; + } + } +} diff --git a/toonz/sources/common/tcore/tthread_nt.cpp b/toonz/sources/common/tcore/tthread_nt.cpp new file mode 100644 index 0000000..3739a3f --- /dev/null +++ b/toonz/sources/common/tcore/tthread_nt.cpp @@ -0,0 +1,387 @@ + + +#include "tthread.h" +#define _WIN32_WINNT 0x0400 +#include + +using std::list; + +class TThreadGroupImp; +//--------------------------------------------------------------------------- +// TMutex & TMutexImp +//--------------------------------------------------------------------------- + +class TMutexImp +{ + HANDLE id; + +public: + TMutexImp(); + ~TMutexImp(); + void lock(); + void unlock(); +}; + +//--------------------------------------------------------------------------- + +class TThreadGroupImp +{ + list threads; + +public: + TThreadGroupImp(); + ~TThreadGroupImp(); + void add(TThread *); + void remove(TThread *); + void wait(); +}; +//--------------------------------------------------------------------------- + +TMutexImp::TMutexImp() +{ + id = CreateMutex(NULL, FALSE, NULL); +} + +//--------------------------------------------------------------------------- + +TMutexImp::~TMutexImp() +{ + BOOL rc = CloseHandle(id); +} +//--------------------------------------------------------------------------- + +void TMutexImp::lock() +{ + DWORD rc = WaitForSingleObject(id, INFINITE); +} + +//--------------------------------------------------------------------------- + +void TMutexImp::unlock() +{ + BOOL rc = ReleaseMutex(id); +} + +//--------------------------------------------------------------------------- + +TMutex::TMutex() + : m_imp(new TMutexImp()) +{ +} + +//--------------------------------------------------------------------------- + +TMutex::~TMutex() +{ + delete m_imp; +} +//--------------------------------------------------------------------------- + +void TMutex::lock() +{ + m_imp->lock(); +} + +//--------------------------------------------------------------------------- + +void TMutex::unlock() +{ + m_imp->unlock(); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +// TThread & TThreadImp +//--------------------------------------------------------------------------- + +class TThreadImp +{ +public: + friend class TThreadGroupImp; + + HANDLE threadId; + HANDLE mainThread; + int m_refCount; + TMutex secureLock; + bool isRunning; + TThreadGroupImp *owner; + TThread *thread; + + //some static stuff + static TUINT32 nThreads; + static TMutex mutex; + + TThreadImp(); + ~TThreadImp(); + + void start(); + + bool setThreadPriority(TThread::TThreadPriority p); + bool setPreferredProcessor(int processorId); + + static void incNThreads() + { + mutex.lock(); + nThreads++; + mutex.unlock(); + } + static void decNThreads() + { + mutex.lock(); + nThreads--; + mutex.unlock(); + } + + void setOwner(TThreadGroupImp *_owner) { owner = _owner; } +}; + +TUINT32 TThreadImp::nThreads = 0; +TMutex TThreadImp::mutex = TMutex(); + +//--------------------------------------------------------------------------- + +TThreadImp::TThreadImp() + : isRunning(false), threadId(0), owner(0), mainThread(0), thread(0), secureLock(), m_refCount(0) +{ +} + +//--------------------------------------------------------------------------- + +TThreadImp::~TThreadImp() +{ + if (threadId) + CloseHandle(threadId); +} + +//--------------------------------------------------------------------------- + +static TUINT32 __stdcall fun(void *data) +{ + TThreadImp *t = (TThreadImp *)data; + + t->secureLock.lock(); + if (t->isRunning) { + t->secureLock.unlock(); + assert(!"thread is already running"); + return 0; + } + t->isRunning = true; + + t->secureLock.unlock(); + + t->thread->run(); + + t->decNThreads(); + if (t->owner) + t->owner->remove(t->thread); + t->thread->release(); + return 0; +} + +//--------------------------------------------------------------------------- + +void TThreadImp::start() +{ + TThreadImp::incNThreads(); + threadId = CreateThread(0, 0, fun, this, 0, 0); +} +//--------------------------------------------------------------------------- + +bool TThreadImp::setThreadPriority(TThread::TThreadPriority p) +{ + int priority; + switch (p) { + case TThread::TIME_CRITICAL: + priority = THREAD_PRIORITY_TIME_CRITICAL; + break; + case TThread::HIGHEST: + priority = THREAD_PRIORITY_HIGHEST; + break; + case TThread::ABOVE_NORMAL: + priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case TThread::NORMAL: + priority = THREAD_PRIORITY_NORMAL; + break; + case TThread::BELOW_NORMAL: + priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case TThread::LOWEST: + priority = THREAD_PRIORITY_LOWEST; + break; + case TThread::IDLE: + priority = THREAD_PRIORITY_IDLE; + break; + default: + priority = THREAD_PRIORITY_NORMAL; + break; + } + return !!SetThreadPriority(threadId, priority); +} + +//--------------------------------------------------------------------------- + +bool TThreadImp::setPreferredProcessor(int processorId) +{ + DWORD rc = SetThreadIdealProcessor(threadId, processorId); + return (rc != -1); +} + +//--------------------------------------------------------------------------- + +TThread::TThread() + : m_imp(new TThreadImp()) +{ + m_imp->thread = this; +} + +//--------------------------------------------------------------------------- + +TThread::~TThread() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TThread::start() +{ + addRef(); + m_imp->start(); +} + +//--------------------------------------------------------------------------- + +void TThread::addRef() +{ + m_imp->mutex.lock(); + m_imp->m_refCount++; + m_imp->mutex.unlock(); +} + +//--------------------------------------------------------------------------- + +void TThread::release() +{ + bool kill = false; + m_imp->mutex.lock(); + m_imp->m_refCount--; + if (m_imp->m_refCount <= 0) + kill = true; + m_imp->mutex.unlock(); + if (kill) + delete this; +} + +//--------------------------------------------------------------------------- + +int TThread::getRefCount() +{ + return m_imp->m_refCount; +} + +//--------------------------------------------------------------------------- + +bool TThread::setPreferredProcessor(int processorId) +{ + return m_imp->setPreferredProcessor(processorId); +} + +//--------------------------------------------------------------------------- + +bool TThread::setThreadPriority(TThread::TThreadPriority p) +{ + return m_imp->setThreadPriority(p); +} + +//======================= +// TThreadGroupImp + +//--------------------------------------------------------------------------- + +TThreadGroupImp::TThreadGroupImp() +{ +} + +//--------------------------------------------------------------------------- + +TThreadGroupImp::~TThreadGroupImp() +{ +} + +//--------------------------------------------------------------------------- +void TThreadGroupImp::add(TThread *t) +{ + threads.push_back(t); + t->m_imp->setOwner(this); +} +//--------------------------------------------------------------------------- + +void TThreadGroupImp::remove(TThread *t) +{ + threads.remove(t); + t->m_imp->setOwner(0); +} +//--------------------------------------------------------------------------- + +void TThreadGroupImp::wait() +{ + DWORD count = threads.size(); + if (count == 0) + return; + HANDLE *hThreads = new HANDLE[count]; + int id = 0; + for (list::iterator it = threads.begin(); it != threads.end(); it++, id++) { + TThread *t = *it; + if (t->m_imp->threadId == 0) + t->start(); + hThreads[id] = t->m_imp->threadId; + } + + DWORD rc = WaitForMultipleObjects(count, hThreads, FALSE, INFINITE); + if (rc >= WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + count - 1)) { + // cout << "obj #" << rc << endl; + } + if (rc >= WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + count - 1)) { + // cout << "obj #" << rc << " abandoned" << endl; + } + if (rc == WAIT_TIMEOUT) { + // cout << "timeout" << endl; + } + + if (rc == WAIT_FAILED) { + // cout << "failed" << endl; + } + + delete[] hThreads; + wait(); +} + +//--------------------------------------------------------------------------- + +TThreadGroup::TThreadGroup() + : m_imp(new TThreadGroupImp()) + +{ +} + +//--------------------------------------------------------------------------- + +TThreadGroup::~TThreadGroup() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TThreadGroup::add(TThread *t) +{ + m_imp->add(t); +} + +//--------------------------------------------------------------------------- + +void TThreadGroup::wait() +{ + m_imp->wait(); +} diff --git a/toonz/sources/common/tcore/tthread_x.cpp b/toonz/sources/common/tcore/tthread_x.cpp new file mode 100644 index 0000000..87adb19 --- /dev/null +++ b/toonz/sources/common/tcore/tthread_x.cpp @@ -0,0 +1,347 @@ + + +#include "tthread.h" +#include + +//--------------------------------------------------------------------------- +// TMutex & TMutexImp +//--------------------------------------------------------------------------- + +class TMutexImp +{ + pthread_mutex_t id; + +public: + TMutexImp(); + ~TMutexImp(); + void lock(); + void unlock(); +}; + +//--------------------------------------------------------------------------- +TMutex lockForTheList; + +class TThreadGroupImp +{ + list threads; + +public: + TThreadGroupImp(); + ~TThreadGroupImp(); + void add(TThread *); + void remove(TThread *); + void wait(); +}; + +//--------------------------------------------------------------------------- + +TMutexImp::TMutexImp() +{ + pthread_mutex_init(&id, 0); +} + +//--------------------------------------------------------------------------- + +TMutexImp::~TMutexImp() +{ + pthread_mutex_destroy(&id); +} + +//--------------------------------------------------------------------------- + +void TMutexImp::lock() +{ + pthread_mutex_lock(&id); +} + +//--------------------------------------------------------------------------- + +void TMutexImp::unlock() +{ + pthread_mutex_unlock(&id); +} + +//--------------------------------------------------------------------------- + +TMutex::TMutex() + : m_imp(new TMutexImp) +{ +} + +//--------------------------------------------------------------------------- + +TMutex::~TMutex() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TMutex::lock() +{ + m_imp->lock(); +} + +//--------------------------------------------------------------------------- + +void TMutex::unlock() +{ + m_imp->unlock(); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +// TThread & TThreadImp +//--------------------------------------------------------------------------- + +class TThreadImp +{ + pthread_t threadId; + +public: + TThreadImp(); + ~TThreadImp(); + + TThread *thread; + + void start(); + + bool setThreadPriority(TThread::TThreadPriority p); + bool setPreferredProcessor(int processorId); + + TMutex secureLock; + bool isRunning; + + static void incNThreads() + { + mutex.lock(); + nThreads++; + mutex.unlock(); + } + static void decNThreads() + { + mutex.lock(); + nThreads--; + mutex.unlock(); + } + + //some static stuff + static TUINT32 nThreads; + static TMutex mutex; + + friend class TThreadGroupImp; + void setOwner(TThreadGroupImp *_owner) { owner = _owner; } + TThreadGroupImp *owner; +}; + +TUINT32 TThreadImp::nThreads = 0; +TMutex TThreadImp::mutex = TMutex(); +//--------------------------------------------------------------------------- + +TThreadImp::TThreadImp() + : isRunning(false), owner(0), thread(0) +{ +} + +//--------------------------------------------------------------------------- + +TThreadImp::~TThreadImp() +{ + //CloseHandle(threadId); +} +//--------------------------------------------------------------------------- + +static void * /*__stdcall*/ fun(void *data) +{ + TThreadImp *t = (TThreadImp *)data; + + t->secureLock.lock(); + if (t->isRunning) { + t->secureLock.unlock(); + assert(!"thread is already running"); + return 0; + } + t->isRunning = true; + + t->secureLock.unlock(); + + t->thread->run(); + + t->decNThreads(); + if (t->owner) + t->owner->remove(t->thread); + + return 0; +} + +//--------------------------------------------------------------------------- + +void TThreadImp::start() +{ + incNThreads(); + pthread_create(&threadId, 0, fun, (void *)this); +} + +//--------------------------------------------------------------------------- + +bool TThreadImp::setThreadPriority(TThread::TThreadPriority) +{ + assert(!"not implemented"); + return false; +} + +//--------------------------------------------------------------------------- + +bool TThreadImp::setPreferredProcessor(int processorId) +{ +#ifdef __sgi +#if (OP_RELEASE == rel_2) + assert(!"Not implemented"); + return false; +#else + int rc = pthread_setrunon_np(processorId); + return (rc != -1); +#endif +#else + assert(0); + return false; +#endif +} + +//--------------------------------------------------------------------------- + +TThread::TThread() + : m_imp(new TThreadImp()) +{ + m_imp->thread = this; +} + +//--------------------------------------------------------------------------- + +TThread::~TThread() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TThread::start() +{ + m_imp->start(); +} + +//--------------------------------------------------------------------------- + +bool TThread::setPreferredProcessor(int processorId) +{ + return m_imp->setPreferredProcessor(processorId); +} + +//--------------------------------------------------------------------------- + +bool TThread::setThreadPriority(TThread::TThreadPriority p) +{ + return m_imp->setThreadPriority(p); +} + +//======================= +// TThreadGroupImp + +TThreadGroupImp::TThreadGroupImp() +{ +} + +//--------------------------------------------------------------------------- + +TThreadGroupImp::~TThreadGroupImp() +{ +} + +//--------------------------------------------------------------------------- + +void TThreadGroupImp::add(TThread *t) +{ + lockForTheList.lock(); + threads.push_back(t); + lockForTheList.unlock(); + t->m_imp->setOwner(this); +} +//--------------------------------------------------------------------------- + +void TThreadGroupImp::remove(TThread *t) +{ + lockForTheList.lock(); + threads.remove(t); + lockForTheList.unlock(); + t->m_imp->setOwner(0); +} +//--------------------------------------------------------------------------- + +static void * /*__stdcall*/ mainFun(void *data) +{ + //cout << "mainfun" << endl; + list *threads = (list *)data; + //lockForTheList.lock(); + ULONG s = threads->size(); + //lockForTheList.unlock(); + //cout <<"ci sono " << s << "thread in ballo..." << endl; + + while (s != 0) { + lockForTheList.lock(); + s = threads->size(); + lockForTheList.unlock(); + } + return 0; +} + +//--------------------------------------------------------------------------- +void TThreadGroupImp::wait() +{ + //cout << "wait()" << endl; + lockForTheList.lock(); + ULONG count = threads.size(); + + for (list::iterator it = threads.begin(); it != threads.end(); it++) { + TThread *t = *it; + t->start(); + } + lockForTheList.unlock(); + + if (count == 0) + return; + void *mainRet = 0; + pthread_t mainThread; + //cout << "creo il main" << endl; + pthread_create(&mainThread, 0, mainFun, &threads); + //cout << mainThread << endl; + pthread_join(mainThread, &mainRet); +} + +//--------------------------------------------------------------------------- + +TThreadGroup::TThreadGroup() + : m_imp(new TThreadGroupImp()) + +{ +} + +//--------------------------------------------------------------------------- + +TThreadGroup::~TThreadGroup() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TThreadGroup::add(TThread *t) +{ + m_imp->add(t); +} + +//--------------------------------------------------------------------------- + +void TThreadGroup::wait() +{ + m_imp->wait(); +} diff --git a/toonz/sources/common/tcore/tthreadp.h b/toonz/sources/common/tcore/tthreadp.h new file mode 100644 index 0000000..a8fc692 --- /dev/null +++ b/toonz/sources/common/tcore/tthreadp.h @@ -0,0 +1,74 @@ + + +#ifndef TTHREADP_H +#define TTHREADP_H + +#include + +#include "tthreadmessage.h" + +//===================================================================== + +//====================================== +// TThreadMessageDispatcher class +//-------------------------------------- + +//NOTE: This class should eventually be moved to tthreadmessagep.h... +class TThreadMessageDispatcher : public QObject //singleton +{ + Q_OBJECT + +public: + TThreadMessageDispatcher(); + Q_SIGNALS : void signaled(TThread::Message *msg); + void blockingSignaled(TThread::Message *msg); +protected Q_SLOTS: + void onSignal(TThread::Message *msg); + +public: + void emitSignaled(TThread::Message *msg); + void emitBlockingSignaled(TThread::Message *msg); + static void init(); + static TThreadMessageDispatcher *instance(); +}; + +//===================================================================== + +namespace TThread +{ +//Forward declarations +class ExecutorId; +class ExecutorImpSlots; +} + +//===================================================================== + +//============================== +// ExecutorImpSlots class +//------------------------------ + +class TThread::ExecutorImpSlots : public QObject +{ + Q_OBJECT + +public: + ExecutorImpSlots(); + ~ExecutorImpSlots(); + + //The following is provided to ensure that point #3 in Qt reference in page "Thread support in Qt" + //is satisfied: + + // "You must ensure that all objects created in + // a thread are deleted before you delete the QThread." + + //So, specifically, thread creation should happen only in the main thread, not in worker threads. + void emitRefreshAssignments(); + + Q_SIGNALS : void refreshAssignments(); + +public Q_SLOTS: + void onTerminated(); + void onRefreshAssignments(); +}; + +#endif diff --git a/toonz/sources/common/tcore/tundo.cpp b/toonz/sources/common/tcore/tundo.cpp new file mode 100644 index 0000000..ebfcc31 --- /dev/null +++ b/toonz/sources/common/tcore/tundo.cpp @@ -0,0 +1,463 @@ + + +#include "tundo.h" +#include + +//----------------------------------------------------------------------------- + +using std::for_each; + +namespace +{ + +void deleteUndo(const TUndo *undo) { delete undo; } +void callUndo(const TUndo *undo) { undo->undo(); } +void callRedo(const TUndo *undo) { undo->redo(); } +// void callRepeat(const TUndo* undo) {undo->repeat(); } + +class TUndoBlock : public TUndo +{ + vector m_undos; + typedef std::vector::const_iterator Iterator; + typedef std::vector::const_reverse_iterator ReverseIterator; + mutable bool m_deleted, m_undoing; + +public: + TUndoBlock() : m_deleted(false), m_undoing(false) {} + ~TUndoBlock() + { + assert(m_undoing == false); + assert(m_deleted == false); + m_deleted = true; + for_each(m_undos.begin(), m_undos.end(), deleteUndo); + m_undos.clear(); + } + + int getSize() const + { + int size = sizeof(*this); + for (Iterator cit = m_undos.begin(); cit != m_undos.end(); ++cit) + size += (*cit)->getSize(); + size += (m_undos.capacity() - m_undos.size()) * sizeof(TUndo *); + return size; + } + int getUndoCount() const + { + return (int)m_undos.size(); + } + void setLast() + { + for (UINT i = 1; i < m_undos.size(); i++) + m_undos[i]->m_isLastInBlock = false; + m_undos[0]->m_isLastInBlock = true; + } + + void undo() const + { + assert(!m_deleted); + assert(!m_undoing); + m_undoing = true; + //VERSIONE CORRETTA + for_each(m_undos.rbegin(), m_undos.rend(), callUndo); + //VERSIONE SBAGLIATA + //for_each(m_undos.begin(), m_undos.end(), callUndo); + m_undoing = false; + } + void redo() const + { + assert(!m_deleted); + //VERSIONE CORRETTA + for_each(m_undos.begin(), m_undos.end(), callRedo); + //VERSIONE SBAGLIATA + //for_each(m_undos.rbegin(), m_undos.rend(), callRedo); + } + + //void repeat() const { + // for_each(m_undos.begin(), m_undos.end(), callRepeat); + //} + void onAdd() {} + void add(TUndo *undo) + { + undo->m_isLastInBlock = true; + m_undos.push_back(undo); + } + + void popUndo(int n) + { + if (n == -1) + n = m_undos.size(); + if (m_undos.empty() || n <= 0) + return; + while (n > 0 && !m_undos.empty()) { + TUndo *undo = m_undos.back(); + m_undos.pop_back(); + delete undo; + n--; + } + } + + virtual QString getHistoryString() + { + if (m_undos.empty()) + return TUndo::getHistoryString(); + else if ((int)m_undos.size() == 1) + return m_undos.back()->getHistoryString(); + else { + return QString("%1 etc..").arg(m_undos.back()->getHistoryString()); + } + } + + virtual int getHistoryType() + { + if (m_undos.empty()) + return TUndo::getHistoryType(); + else + return m_undos.back()->getHistoryType(); + } +}; +} + +typedef std::deque UndoList; +typedef UndoList::iterator UndoListIterator; +typedef UndoList::const_iterator UndoListConstIterator; + +//----------------------------------------------------------------------------- + +struct TUndoManager::TUndoManagerImp { + UndoList m_undoList; + UndoListIterator m_current; + bool m_skipped; + int m_undoMemorySize; // in bytes + + vector m_blockStack; + +public: + TUndoManagerImp() : m_skipped(false), m_undoMemorySize(0) { m_current = m_undoList.end(); } + ~TUndoManagerImp() {} + + void add(TUndo *undo); + +public: + static struct ManagerPtr { + TUndoManager *m_ptr; + + public: + ManagerPtr() : m_ptr(0) {} + ~ManagerPtr() + { + if (m_ptr) + delete m_ptr; + m_ptr = 0; + } + + } theManager; + +private: + void doAdd(TUndo *undo); +}; + +//============================================================================= + +TUndoManager::TUndoManagerImp::ManagerPtr TUndoManager::TUndoManagerImp::theManager; + +//----------------------------------------------------------------------------- + +TUndoManager *TUndoManager::manager() +{ + if (!TUndoManagerImp::theManager.m_ptr) + TUndoManagerImp::theManager.m_ptr = new TUndoManager; + return TUndoManagerImp::theManager.m_ptr; +} + +//============================================================================= + +TUndoManager::TUndoManager() + : m_imp(new TUndoManagerImp) +{ + // cout << "Creato undo manager" << endl; +} + +//----------------------------------------------------------------------------- + +TUndoManager::~TUndoManager() +{ + //cout << "Distrutto undo manager" << endl; + assert(m_imp->m_blockStack.empty()); + reset(); + delete m_imp; +} + +//----------------------------------------------------------------------------- + +void TUndoManager::TUndoManagerImp::add(TUndo *undo) +{ + assert(undo); + + if (!m_blockStack.empty()) { + assert(m_current == m_undoList.end()); + m_blockStack.back()->add(undo); + } else + doAdd(undo); +} + +//----------------------------------------------------------------------------- + +void TUndoManager::TUndoManagerImp::doAdd(TUndo *undo) +{ + if (m_current != m_undoList.end()) { + for_each(m_current, m_undoList.end(), deleteUndo); + m_undoList.erase(m_current, m_undoList.end()); + } + + int i, memorySize = 0, count = m_undoList.size(); + for (i = 0; i < count; i++) + memorySize += m_undoList[i]->getSize(); + + while (count > 100 || (count != 0 && memorySize + undo->getSize() > m_undoMemorySize)) //20MB + { + --count; + TUndo *undo = m_undoList.front(); + m_undoList.pop_front(); + memorySize -= undo->getSize(); + delete undo; + } + + undo->m_isLastInBlock = true; + m_undoList.push_back(undo); + m_current = m_undoList.end(); +} + +//----------------------------------------------------------------------------- + +void TUndoManager::beginBlock() +{ + if (m_imp->m_current != m_imp->m_undoList.end()) { + for_each(m_imp->m_current, m_imp->m_undoList.end(), deleteUndo); + m_imp->m_undoList.erase(m_imp->m_current, m_imp->m_undoList.end()); + } + + TUndoBlock *undoBlock = new TUndoBlock; + m_imp->m_blockStack.push_back(undoBlock); + m_imp->m_current = m_imp->m_undoList.end(); +} + +//----------------------------------------------------------------------------- + +void TUndoManager::endBlock() +{ + //vogliamo fare anche resize del vector ??? + assert(m_imp->m_blockStack.empty() == false); + TUndoBlock *undoBlock = m_imp->m_blockStack.back(); + m_imp->m_blockStack.pop_back(); + if (undoBlock->getUndoCount() > 0) { + undoBlock->setLast(); + m_imp->add(undoBlock); + emit historyChanged(); + } else { + delete undoBlock; + m_imp->m_current = m_imp->m_undoList.end(); + } +} + +//----------------------------------------------------------------------------- + +bool TUndoManager::undo() +{ + assert(m_imp->m_blockStack.empty()); + UndoListIterator &it = m_imp->m_current; + if (it != m_imp->m_undoList.begin()) { + m_imp->m_skipped = false; + --it; + (*it)->undo(); + emit historyChanged(); + if (m_imp->m_skipped) { + m_imp->m_skipped = false; + return undo(); + } + return true; + } else + return false; +} + +//----------------------------------------------------------------------------- + +bool TUndoManager::redo() +{ + assert(m_imp->m_blockStack.empty()); + UndoListIterator &it = m_imp->m_current; + if (it != m_imp->m_undoList.end()) { + m_imp->m_skipped = false; + (*it)->redo(); + ++it; + emit historyChanged(); + if (m_imp->m_skipped) { + m_imp->m_skipped = false; + return redo(); + } + return true; + } else + return false; +} + +//----------------------------------------------------------------------------- +//repeat e' come redo ma non sposta il puntatore al corrente +/* +void TUndoManager::repeat() +{ + assert(m_imp->m_blockStack.empty()); + UndoListIterator &it = m_imp->m_current; + if (it != m_imp->m_undoList.end()) + { + (*it)->repeat(); + } +} +*/ + +//----------------------------------------------------------------------------- + +void TUndoManager::skip() +{ + m_imp->m_skipped = true; +} + +//----------------------------------------------------------------------------- + +void TUndoManager::setUndoMemorySize(int memorySyze) +{ + m_imp->m_undoMemorySize = 1048576 * memorySyze; +} + +//----------------------------------------------------------------------------- + +void TUndoManager::add(TUndo *undo) +{ + assert(undo); + if (!undo) + return; + + m_imp->add(undo); + Q_EMIT historyChanged(); + + undo->onAdd(); + Q_EMIT somethingChanged(); +} + +//----------------------------------------------------------------------------- + +void TUndoManager::reset() +{ + assert(m_imp->m_blockStack.empty()); + m_imp->m_blockStack.clear(); + UndoList &lst = m_imp->m_undoList; + for_each(lst.begin(), lst.end(), deleteUndo); + lst.clear(); + m_imp->m_current = m_imp->m_undoList.end(); + Q_EMIT historyChanged(); +} + +//----------------------------------------------------------------------------- + +void TUndoManager::popUndo(int n, bool forward) +{ + if (!forward) { + if (m_imp->m_blockStack.empty()) { + if (n == -1) { + UndoListIterator start = m_imp->m_undoList.begin(); + n = 0; + while (start != m_imp->m_current) + ++n; + } + if (m_imp->m_undoList.empty() || n <= 0) + return; + if (m_imp->m_current == m_imp->m_undoList.end()) { + int i; + for (i = 0; i < n && m_imp->m_current != m_imp->m_undoList.begin(); i++) { + m_imp->m_current--; + delete (*m_imp->m_current); + m_imp->m_undoList.erase(m_imp->m_current); + m_imp->m_current = m_imp->m_undoList.end(); + } + } else { + TUndo *undo = *m_imp->m_current; + UndoListIterator start, end = m_imp->m_current; + if (end == m_imp->m_undoList.begin()) + return; + + int i; + for (i = 0; i < n && m_imp->m_current != m_imp->m_undoList.begin(); i++) + m_imp->m_current--; + + start = m_imp->m_current; + UndoListIterator _end = end; + while (_end != start) { + _end--; + delete (*_end); + } + m_imp->m_undoList.erase(start, end); + + m_imp->m_current = m_imp->m_undoList.begin(); + while (*m_imp->m_current != undo) + m_imp->m_current++; + } + } else + m_imp->m_blockStack.back()->popUndo(n); + return; + } + + if (m_imp->m_current == m_imp->m_undoList.end()) + return; + if (m_imp->m_blockStack.empty()) { + UndoListIterator end, start = m_imp->m_current++; + if (n == -1) + end = m_imp->m_undoList.end(); + else { + UndoListIterator it = start; + end = it; + int i = 0; + while (i != n && end != m_imp->m_undoList.end()) { + ++end; + i++; + } + } + for_each(start, end, deleteUndo); + m_imp->m_undoList.erase(start, end); + m_imp->m_current = m_imp->m_undoList.end(); + } else + m_imp->m_blockStack.back()->popUndo(n); +} + +//----------------------------------------------------------------------------- + +int TUndoManager::getHistoryCount() +{ + return (int)m_imp->m_undoList.size(); +} + +//----------------------------------------------------------------------------- + +int TUndoManager::getCurrentHistoryIndex() +{ + int index = 0; + UndoListIterator it = m_imp->m_undoList.begin(); + while (1) { + if (it == m_imp->m_current) + return index; + + if (it == m_imp->m_undoList.end()) + break; + + index++; + it++; + } + return 0; +} + +//----------------------------------------------------------------------------- + +TUndo *TUndoManager::getUndoItem(int index) +{ + if (index > (int)m_imp->m_undoList.size() || index <= 0) + return 0; + + return m_imp->m_undoList.at(index - 1); +} diff --git a/toonz/sources/common/tfx/binaryFx.cpp b/toonz/sources/common/tfx/binaryFx.cpp new file mode 100644 index 0000000..e24e380 --- /dev/null +++ b/toonz/sources/common/tfx/binaryFx.cpp @@ -0,0 +1,774 @@ + + +// TnzCore includes +#include "tstream.h" +#include "trop.h" +#include "tflash.h" + +// TnzBase includes +#include "tdoubleparam.h" +#include "tnotanimatableparam.h" +#include "tfxparam.h" +#include "trasterfx.h" +#include "tbasefx.h" + +//****************************************************************************************** +// Local namespace +//****************************************************************************************** + +namespace +{ +void makeRectCoherent(TRectD &rect, const TPointD &pos) +{ + rect -= pos; + rect.x0 = tfloor(rect.x0); + rect.y0 = tfloor(rect.y0); + rect.x1 = tceil(rect.x1); + rect.y1 = tceil(rect.y1); + rect += pos; +} +} + +//****************************************************************************************** +// TImageCombinationFx declaration +//****************************************************************************************** + +class TImageCombinationFx : public TBaseRasterFx +{ + TFxPortDG m_group; + +public: + TImageCombinationFx(); + virtual ~TImageCombinationFx() {} + +public: + // Virtual interface for heirs + + //! The raster processing function that must be reimplemented to perform the fx + virtual void process(const TRasterP &up, const TRasterP &down, double frame) = 0; + + //! Whether the 'up' rasters of process() invocations must be allocated to + //! entirely cover the 'down' counterpart. Should be enabled if the process() + //! function affects 'down' pixels when the 'up's are fully transparent. + virtual bool requiresFullRect() { return false; } + +public: + // Low-level TRasterFx-related functions + + int dynamicPortGroupsCount() const { return 1; } + const TFxPortDG *dynamicPortGroup(int g) const { return (g == 0) ? &m_group : 0; } + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) + { + // At max the memory of another tile with the same infos may be allocated apart from + // the externally supplied one. + return TRasterFx::memorySize(rect, info.m_bpp); + } + + bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info); + + void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info); + void doCompute(TTile &tile, double frame, const TRenderSettings &info); + + void compatibilityTranslatePort(int majorVersion, int minorVersion, std::string &portName); + + int getPreferredInputPort() { return 1; } +}; + +//****************************************************************************************** +// TImageCombinationFx implementation +//****************************************************************************************** + +TImageCombinationFx::TImageCombinationFx() + : m_group("Source", 2) +{ + addInputPort("Source1", new TRasterFxPort, 0); + addInputPort("Source2", new TRasterFxPort, 0); + setName(L"ImageCombinationFx"); +} + +//--------------------------------------------------------------------------- + +bool TImageCombinationFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) +{ + bBox = TRectD(); + + int p, pCount = getInputPortCount(); + for (p = 0; p != pCount; ++p) { + TRasterFxPort *port = static_cast(getInputPort(p)); + TRectD inputBBox; + + bool hasInput = (port && port->isConnected()) ? (*port)->doGetBBox(frame, inputBBox, info) : false; + + if (hasInput) + bBox += inputBBox; + } + + return (bBox.getLx() >= 0) && (bBox.getLy() >= 0); +} + +//--------------------------------------------------------------------------- + +void TImageCombinationFx::doCompute(TTile &tile, double frame, const TRenderSettings &info) +{ + int p, pCount = getInputPortCount(); + TRasterFxPort *port = 0; + + // Skip empty ports + for (p = pCount - 1; p >= 0; --p) // Reverse iteration - bottom ports have high indices + { + port = static_cast(getInputPort(p)); + if (port && port->getFx()) + break; + } + + // If there is no input, clear and return + if (p < 0) { + tile.getRaster()->clear(); // Probably not necessary unless externally invocation + return; // deliberately soiled tile - however, should be rare anyway. + } + + // Calculate the tiles' geometries + const TRect &tileRect(tile.getRaster()->getBounds()); + const TDimension &tileSize(tileRect.getSize()); + TRectD tileRectD(tile.m_pos, TDimensionD(tileSize.lx, tileSize.ly)); + + // Render the first viable port directly on tile + (*port)->compute(tile, frame, info); // Should we do it only if the bbox is not empty? + + // Then, render each subsequent port and process() it on top of tile + bool canRestrict = !requiresFullRect(); + + for (--p; p >= 0; --p) { + port = static_cast(getInputPort(p)); + if (!(port && port->getFx())) // Skip empty ports + continue; + + // Will allocate a new tile to calculate the input contribution - so, if possible + // we'll restrict allocation to the input port's bbox + TRectD computeRect(tileRectD); + + if (canRestrict) { + TRectD inBBox; + (*port)->getBBox(frame, inBBox, info); + + computeRect *= inBBox; + + makeRectCoherent(computeRect, tile.m_pos); // Make it coherent with tile's pixel geometry + } + + // Calculate the input port and perform processing + TDimension computeSize(tround(computeRect.getLx()), tround(computeRect.getLy())); + if ((computeSize.lx > 0) && (computeSize.ly > 0)) { + TTile inTile; // Observe its locality - not incidental + (*port)->allocateAndCompute(inTile, computeRect.getP00(), computeSize, + tile.getRaster(), frame, info); + + // Invoke process() to deal with the actual fx processing + TRasterP up(inTile.getRaster()), down(tile.getRaster()); + + if (canRestrict) { + // Extract from tile the part corresponding to inTile + TRect downRect(convert(computeRect.getP00() - tile.m_pos), computeSize); + down = down->extract(downRect); + } + + assert(up->getSize() == down->getSize()); + process(up, down, frame); // This is the point with the max concentration + // of allocated resources + } + } +} + +//------------------------------------------------------------------------------------- + +void TImageCombinationFx::doDryCompute( + TRectD &rect, double frame, const TRenderSettings &info) +{ + // Mere copy of doCompute(), stripped of the actual computations + + int p, pCount = getInputPortCount(); + TRasterFxPort *port = 0; + + for (p = pCount - 1; p >= 0; --p) { + port = static_cast(getInputPort(p)); + if (port && port->getFx()) + break; + } + + if (p < 0) + return; + + (*port)->dryCompute(rect, frame, info); + + bool canRestrict = !requiresFullRect(); + + for (--p; p >= 0; --p) { + port = static_cast(getInputPort(p)); + if (!(port && port->getFx())) + continue; + + TRectD computeRect(rect); + + if (canRestrict) { + TRectD inBBox; + (*port)->getBBox(frame, inBBox, info); + + computeRect *= inBBox; + makeRectCoherent(computeRect, rect.getP00()); + } + + TDimension computeSize(tround(computeRect.getLx()), tround(computeRect.getLy())); + if ((computeSize.lx > 0) && (computeSize.ly > 0)) + (*port)->dryCompute(computeRect, frame, info); + } +} + +//------------------------------------------------------------------------------------- + +void TImageCombinationFx::compatibilityTranslatePort(int major, int minor, std::string &portName) +{ + if (VersionNumber(major, minor) < VersionNumber(1, 20)) { + if (portName == "Up") + portName = "Source1"; + else if (portName == "Down") + portName = "Source2"; + } +} + +//****************************************************************************************** +// TImageCombinationFx heir classes +//****************************************************************************************** + +class OverFx : public TImageCombinationFx +{ + + FX_DECLARATION(OverFx) + +public: + OverFx() + { + setName(L"OverFx"); + } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::over(down, up); + } +}; + +//================================================================== + +class AddFx : public TImageCombinationFx +{ + FX_DECLARATION(AddFx) + + TDoubleParamP m_value; + +public: + AddFx() : m_value(100.0) + { + bindParam(this, "value", m_value); + } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + double value = m_value->getValue(frame) / 100.0; + + if (value != 1.0) + TRop::add(up, down, down, value); + else + TRop::add(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class ColorDodgeFx : public TImageCombinationFx +{ + FX_DECLARATION(AddFx) + +public: + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::colordodge(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class ColorBurnFx : public TImageCombinationFx +{ + FX_DECLARATION(AddFx) + +public: + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::colorburn(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class ScreenFx : public TImageCombinationFx +{ + FX_DECLARATION(AddFx) + +public: + bool requiresFullRect() { return true; } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::screen(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class SubFx : public TImageCombinationFx +{ + FX_DECLARATION(SubFx) + + TBoolParamP m_matte; + +public: + SubFx() : m_matte(false) + { + bindParam(this, "matte", m_matte); + } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::sub(up, down, down, m_matte->getValue()); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class MultFx : public TImageCombinationFx +{ + FX_DECLARATION(MultFx) + + TDoubleParamP m_value; + TBoolParamP m_matte; + +public: + MultFx() : m_value(0.0), m_matte(false) + { + bindParam(this, "value", m_value); + bindParam(this, "matte", m_matte); + } + + bool requiresFullRect() { return m_matte->getValue(); } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::mult(up, down, down, m_value->getValue(frame), m_matte->getValue()); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class MinFx : public TImageCombinationFx +{ + FX_DECLARATION(MinFx) + + TBoolParamP m_matte; + +public: + MinFx() : m_matte(true) + { + bindParam(this, "matte", m_matte); + } + + bool requiresFullRect() { return true; } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::ropmin(up, down, down, m_matte->getValue()); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class MaxFx : public TImageCombinationFx +{ + FX_DECLARATION(MaxFx) + +public: + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::ropmax(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +class LinearBurnFx : public TImageCombinationFx +{ + FX_DECLARATION(LinearBurnFx) + +public: + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::linearburn(up, down, down); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//================================================================== + +//This Fx is probably unused...! +class OverlayFx : public TImageCombinationFx +{ + FX_DECLARATION(OverlayFx) + +public: + OverlayFx() {} + ~OverlayFx() {} + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + TRop::overlay(up, down, down); + } +}; + +//================================================================== + +class BlendFx : public TImageCombinationFx +{ + FX_DECLARATION(BlendFx) + + TDoubleParamP m_value; + +public: + BlendFx() : m_value(0.0) + { + bindParam(this, "value", m_value); + m_value->setValueRange(0.0, 100.0); + } + + bool requiresFullRect() { return true; } + + void process(const TRasterP &up, const TRasterP &down, double frame) + { + double value = 0.01 * m_value->getValue(frame); + UCHAR matteValue = (UCHAR)(value * 255.0 + 0.5); + + TRop::crossDissolve(up, down, down, matteValue); + } + + TFxPort *getXsheetPort() const + { + return getInputPort(1); + } +}; + +//****************************************************************************************** +// Matte Fxs definition +//****************************************************************************************** + +class InFx : public TBaseRasterFx +{ + FX_DECLARATION(InFx) + + TRasterFxPort m_source, m_matte; + +public: + InFx() + { + addInputPort("Source", m_source); + addInputPort("Matte", m_matte); + setName(L"InFx"); + } + + ~InFx() {} + + bool doGetBBox(double frame, TRectD &bbox, const TRenderSettings &info) + { + if (m_matte.isConnected() && m_source.isConnected()) { + bool ret = m_matte->doGetBBox(frame, bbox, info); + + if (bbox == TConsts::infiniteRectD) + return m_source->doGetBBox(frame, bbox, info); + else + return ret; + } + bbox = TRectD(); + return false; + } + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) + { + //This fx is not visible if either the source or the matte tiles are empty. + //It's because only source is visible, and only where matte is opaque. + if (!(m_source.isConnected() && m_matte.isConnected())) + return; + + TTile srcTile; + m_source->allocateAndCompute(srcTile, tile.m_pos, tile.getRaster()->getSize(), tile.getRaster(), frame, ri); + + m_matte->compute(tile, frame, ri); + + TRop::ropin(srcTile.getRaster(), tile.getRaster(), tile.getRaster()); + } + + void doDryCompute(TRectD &rect, + double frame, + const TRenderSettings &info) + { + if (!(m_source.isConnected() && m_matte.isConnected())) + return; + + m_source->dryCompute(rect, frame, info); + m_matte->dryCompute(rect, frame, info); + } + + int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) + { + return TRasterFx::memorySize(rect, info.m_bpp); + } + + void compute(TFlash &flash, int frame) + { + if (m_matte.isConnected()) { + flash.pushMatrix(); + flash.beginMask(); + ((TRasterFxP)(m_matte.getFx()))->compute(flash, frame); + flash.endMask(); + flash.popMatrix(); + } + + if (m_source.isConnected()) { + flash.pushMatrix(); + flash.enableMask(); + ((TRasterFxP)(m_source.getFx()))->compute(flash, frame); + flash.disableMask(); + flash.popMatrix(); + } + } +}; + +//================================================================== + +class OutFx : public TBaseRasterFx +{ + FX_DECLARATION(OutFx) + + TRasterFxPort m_source, m_matte; + +public: + OutFx() + { + addInputPort("Source", m_source); + addInputPort("Matte", m_matte); + setName(L"OutFx"); + } + + ~OutFx() {} + + bool doGetBBox(double frame, TRectD &bbox, const TRenderSettings &info) + { + if (m_source.isConnected()) + return m_source->doGetBBox(frame, bbox, info); + + return false; + } + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) + { + // If there is no source, do nothing + if (!m_source.isConnected()) + return; + + // Here, source is visible where matte is transparent. So if there is + // no matte, just build source. + if (!m_matte.isConnected()) { + m_source->compute(tile, frame, ri); + return; + } + + TTile srcTile; + m_source->allocateAndCompute(srcTile, tile.m_pos, tile.getRaster()->getSize(), tile.getRaster(), frame, ri); + + m_matte->compute(tile, frame, ri); + + TRop::ropout(srcTile.getRaster(), tile.getRaster(), tile.getRaster()); + } + + void doDryCompute(TRectD &rect, + double frame, + const TRenderSettings &info) + { + if (!m_source.isConnected()) + return; + + if (!m_matte.isConnected()) { + m_source->dryCompute(rect, frame, info); + return; + } + + m_source->dryCompute(rect, frame, info); + m_matte->dryCompute(rect, frame, info); + } + + int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) + { + return TRasterFx::memorySize(rect, info.m_bpp); + } +}; + +//================================================================== + +class AtopFx : public TBaseRasterFx +{ + FX_DECLARATION(AtopFx) + + TRasterFxPort m_up, m_dn; + +public: + AtopFx() + { + addInputPort("Up", m_up); + addInputPort("Down", m_dn); + } + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) + { + bBox = TRectD(); + + { + TRectD inputBBox; + + bool hasInput = m_up.isConnected() ? m_up->doGetBBox(frame, inputBBox, info) : false; + if (hasInput) + bBox += inputBBox; + } + + { + TRectD inputBBox; + + bool hasInput = m_dn.isConnected() ? m_dn->doGetBBox(frame, inputBBox, info) : false; + if (hasInput) + bBox += inputBBox; + } + + return (bBox.getLx() >= 0) && (bBox.getLy() >= 0); + } + + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) + { + // Here it's just like matte in, but the matte is visible under up. + + if (!m_dn.isConnected()) + return; + + if (!m_up.isConnected()) { + m_dn->compute(tile, frame, ri); + return; + } + + TTile upTile; + m_up->allocateAndCompute(upTile, tile.m_pos, tile.getRaster()->getSize(), tile.getRaster(), frame, ri); + + m_dn->compute(tile, frame, ri); + + TRop::atop(upTile.getRaster(), tile.getRaster(), tile.getRaster()); + } + + void doDryCompute(TRectD &rect, + double frame, + const TRenderSettings &info) + { + if (!m_dn.isConnected()) + return; + + if (!m_up.isConnected()) { + m_dn->dryCompute(rect, frame, info); + return; + } + + m_up->dryCompute(rect, frame, info); + m_dn->dryCompute(rect, frame, info); + } + + int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) + { + return TRasterFx::memorySize(rect, info.m_bpp); + } +}; + +//================================================================== + +//======================= +// Fx identifiers +//----------------------- + +FX_IDENTIFIER(OverFx, "overFx") +FX_IDENTIFIER(AddFx, "addFx") +FX_IDENTIFIER(SubFx, "subFx") +FX_IDENTIFIER(MultFx, "multFx") +FX_IDENTIFIER(InFx, "inFx") +FX_IDENTIFIER(OutFx, "outFx") +FX_IDENTIFIER(AtopFx, "atopFx") +//FX_IDENTIFIER(XorFx, "xorFx") +FX_IDENTIFIER(MinFx, "minFx") +FX_IDENTIFIER(MaxFx, "maxFx") +FX_IDENTIFIER(LinearBurnFx, "linearBurnFx") +FX_IDENTIFIER(OverlayFx, "overlayFx") +FX_IDENTIFIER(BlendFx, "blendFx") +FX_IDENTIFIER(ColorDodgeFx, "colorDodgeFx") +FX_IDENTIFIER(ColorBurnFx, "colorBurnFx") +FX_IDENTIFIER(ScreenFx, "screenFx") diff --git a/toonz/sources/common/tfx/tcacheresource.cpp b/toonz/sources/common/tfx/tcacheresource.cpp new file mode 100644 index 0000000..4ef73f0 --- /dev/null +++ b/toonz/sources/common/tfx/tcacheresource.cpp @@ -0,0 +1,1009 @@ + + +//String includes +#include "tconvert.h" + +//Image includes +#include "timage_io.h" + +//Toonz Image cache +#include "timagecache.h" + +//TTile class +#include "ttile.h" + +//Trop include +#include "trop.h" + +//File I/O includes +//#include "tstream.h" + +//Qt classes +#include +#include + +//Resources pool manager +#include "tcacheresourcepool.h" + +#include "tcacheresource.h" + +/* +//Debug +#define DIAGNOSTICS +#include "diagnostics.h" + +//Debug stuff +namespace +{ + QString prefix("#resources.txt | RISORSE | "); + QString prefixMem("#memory.txt | "); + + QString traduce(const TRectD& rect) + { + return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + + QString::number(rect.x1) + " " + QString::number(rect.y1) + "]"; + } + + QString traduce(const TRect& rect) + { + return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + + QString::number(rect.x1+1) + " " + QString::number(rect.y1+1) + "]"; + } + + QString traduce(const TTile& tile) + { + TDimension size(tile.getRaster()->getSize()); + TRectD tileRect(tile.m_pos, TDimensionD(size.lx,size.ly)); + return traduce(tileRect); + } +} +*/ + +//==================================================================================================== + +/* +The TCacheResource class models the idea of sparsely defined tile objects. +Whereas common TTile instances can be used to represent images bounded on a specific +rect of the plane, this model does not suit well in case the image object is +sparse across the image plane. +\n \n +The TCacheResource internal representation assumes that the image plane is split +in a fixed-length integer lattice, each cell of which may host an image object in +case some part of the complex inside the cell has been defined. +The plane outside the complex is assumed to be transparent. +\n +The lattice origin must be specified in the complex's contructor, and all tiles +uploaded into and downloaded from it must have an integer displacement with respect +to it - in other words, it is required that the complex has a well-defined pixel geometry. +\n \n +Specific regions of the complex can be cleared through the apposite clear() method. + +\warning +This class does not ensure thread-safety. Concurrent accesses must be dealt by users. +*/ + +//==================================================================================================== + +// Docs stuff + +/*! \fn QRegion TCacheResource::getAvailableRegion() const + +Returns the current region currently available in the complex, with respect to +the complex's origin. +*/ + +/*! \fn int TCacheResource::getRasterType() const + +Returns the type of raster currently present in the tile complex. Only images +providing a raster representation coherent with the complex's one may be exchanged +with it. +*/ + +/*! \fn void TCacheResource::clear() +Clears the whole complex. This is an overloaded method equivalent to +clear(getAvailableRegion()), supplied for convience. +*/ + +//==================================================================================================== + +//**************************************************************************************************** +// Preliminaries +//**************************************************************************************************** + +namespace +{ +//Store tile textures of 512 x 512 pixels. Their memory usage ranges around 1-2 MB each. +const int latticeStep = 512; + +static unsigned long cacheId = 0; + +//----------------------------------------------------------------- + +inline int getRasterType(const TRasterP &ras) +{ + if ((TRaster32P)ras) + return TCacheResource::RGBM32; + else if ((TRaster64P)ras) + return TCacheResource::RGBM64; + else if ((TRasterCM32P)ras) + return TCacheResource::CM32; + + return TCacheResource::NONE; +} + +//----------------------------------------------------------------- + +inline TRasterP getRaster(const TImageP &img) +{ + TRasterImageP rimg(img); + if (rimg) + return rimg->getRaster(); + TToonzImageP timg(img); + if (timg) + return timg->getRaster(); + + assert(!"Wrong image type!"); + return 0; +} + +//----------------------------------------------------------------- + +inline bool isEmpty(const TRect &rect) { return rect.x0 > rect.x1 || rect.y0 > rect.y1; } + +//----------------------------------------------------------------- + +inline QRect toQRect(const TRect &r) { return QRect(r.x0, r.y0, r.getLx(), r.getLy()); } +inline TRect toTRect(const QRect &r) { return TRect(r.left(), r.top(), r.right(), r.bottom()); } +inline QPoint toQPoint(const TPoint &p) { return QPoint(p.x, p.y); } + +//---------------------------------------------------------------------------- + +inline TRect getTileRect(const TTile &tile) +{ + return TRect( + TPoint(tfloor(tile.m_pos.x), tfloor(tile.m_pos.y)), + tile.getRaster()->getSize()); +} + +//---------------------------------------------------------------------------- + +//Qt's contains actually returns QRegion::intersected... I wonder why... +inline bool contains(const QRegion ®ion, const TRect &rect) +{ + return QRegion(toQRect(rect)).subtracted(region).isEmpty(); +} + +//---------------------------------------------------------------------------- + +inline void saveCompressed(const TFilePath &fp, const TRasterP &ras) +{ + assert(ras->getLx() == latticeStep && ras->getLy() == latticeStep); + unsigned int size = sq(latticeStep) * ras->getPixelSize(); + + ras->lock(); + QByteArray data = qCompress((const char *)ras->getRawData(), size); + ras->unlock(); + + Tofstream oss(fp); + oss.write((const char *)&size, sizeof(unsigned int)); + oss.write(data.constData(), data.size()); + assert(!oss.fail()); +} + +//---------------------------------------------------------------------------- + +inline void loadCompressed(const TFilePath &fp, TRasterP &ras, TCacheResource::Type rasType) +{ + Tifstream is(fp); + + if (rasType == TCacheResource::CM32) + ras = TRasterCM32P(latticeStep, latticeStep); + else if (rasType == TCacheResource::RGBM32) + ras = TRaster32P(latticeStep, latticeStep); + else if (rasType == TCacheResource::RGBM64) + ras = TRaster64P(latticeStep, latticeStep); + else + assert(false); + + ras->lock(); + + char *rawData = (char *)ras->getRawData(); + unsigned int dataSize; + is.read((char *)&dataSize, sizeof(unsigned int)); + is.read(rawData, dataSize); + + //Observe that QByteArray::fromRawData does NOT cause a deep copy to occur. + QByteArray data(QByteArray::fromRawData(rawData, dataSize)); + data = qUncompress(data); + memcpy(rawData, data.constData(), data.size()); + + ras->unlock(); +} +} + +//**************************************************************************************************** +// TCacheResourceP implementation +//**************************************************************************************************** + +TCacheResourceP::TCacheResourceP(const std::string &imageName, bool createIfNone) + : m_pointer(TCacheResourcePool::instance()->getResource(imageName, createIfNone)) +{ + if (m_pointer) + m_pointer->addRef(); +} + +//---------------------------------------------------------------------------- + +TCacheResourceP::~TCacheResourceP() +{ + if (m_pointer) { + m_pointer->release(); + m_pointer = 0; + } +} + +//**************************************************************************************************** +// Member functions implementation +//**************************************************************************************************** + +//===================== +// TCacheResource +//--------------------- + +TCacheResource::TCacheResource() + : m_id(cacheId++), m_tileType(NONE), m_cellsCount(0), m_locksCount(0), m_backEnabled(false), m_invalidated(false) +{ +} + +//----------------------------------------------------------------- + +TCacheResource::~TCacheResource() +{ + clear(); +} + +//----------------------------------------------------------------- + +void TCacheResource::release() +{ + if ((--m_refCount) <= 0) { + //Attempt release from the resource pool + TCacheResourcePool::instance()->releaseResource(this); + } +} + +//----------------------------------------------------------------- + +inline TCacheResource::PointLess TCacheResource::getCellIndex(const TPoint &pos) const +{ + return PointLess( + tfloor(pos.x / (double)latticeStep), + tfloor(pos.y / (double)latticeStep)); +} + +//----------------------------------------------------------------- + +inline TPoint TCacheResource::getCellPos(const PointLess &cellIndex) const +{ + return TPoint(cellIndex.x * latticeStep, cellIndex.y * latticeStep); +} + +//----------------------------------------------------------------- + +//Returns the lattice cell containing the position pos. +inline TPoint TCacheResource::getCellPos(const TPoint &pos) const +{ + TPoint cellIndex( + tfloor(pos.x / (double)latticeStep), + tfloor(pos.y / (double)latticeStep)); + return TPoint(cellIndex.x * latticeStep, cellIndex.y * latticeStep); +} + +//----------------------------------------------------------------- + +//Returns the lattice cell containing the relative position pos. +inline TPoint TCacheResource::getCellPos(const TPointD &pos) const +{ + TPoint cellIndex( + tfloor(pos.x / (double)latticeStep), + tfloor(pos.y / (double)latticeStep)); + return TPoint(cellIndex.x * latticeStep, cellIndex.y * latticeStep); +} + +//**************************************************************************************************** +// Tough stuff +//**************************************************************************************************** + +bool TCacheResource::checkRasterType(const TRasterP &ras, int &rasType) const +{ + rasType = ::getRasterType(ras); + if (rasType == NONE) { + assert(!"The passed raster has unkown type!"); + return false; + } + if (m_tileType != NONE && m_tileType != rasType) { + assert(!"The passed raster has not the same type of the cache resource!"); + return false; + } + + return true; +} + +//---------------------------------------------------------------- + +TRasterP TCacheResource::buildCompatibleRaster(const TDimension &size) +{ + TRasterP result; + if (m_tileType == RGBM32) + result = TRaster32P(size); + else if (m_tileType == RGBM64) + result = TRaster64P(size); + else if (m_tileType == CM32) + result = TRasterCM32P(size); + + return result; +} + +//---------------------------------------------------------------- + +bool TCacheResource::checkTile(const TTile &tile) const +{ + //Ensure that tile has integer geoometry. + TPointD tileFracPos(tile.m_pos.x - tfloor(tile.m_pos.x), tile.m_pos.y - tfloor(tile.m_pos.y)); + if (tileFracPos.x != 0.0 || tileFracPos.y != 0.0) { + assert(!"The passed tile must have integer geometry!"); + return false; + } + + return true; +} + +//---------------------------------------------------------------- + +inline std::string TCacheResource::getCellName(int idxX, int idxY) const +{ + return "cell" + toString(idxX) + "," + toString(idxY); +} + +//---------------------------------------------------------------- + +inline std::string TCacheResource::getCellCacheId(int idxX, int idxY) const +{ + return "TCacheResource" + toString(m_id) + getCellName(idxX, idxY); +} + +//---------------------------------------------------------------- + +inline std::string TCacheResource::getCellCacheId(const TPoint &cellPos) const +{ + return getCellCacheId(tfloor(cellPos.x / (double)latticeStep), + tfloor(cellPos.y / (double)latticeStep)); +} + +//----------------------------------------------------------------- + +inline TRasterP TCacheResource::createCellRaster(int rasterType, const std::string &cacheId) +{ + TRasterP result; + + if (rasterType == TCacheResource::NONE) { + assert(!"Unknown raster type!"); + return result; + } + + TImageP img; + if (rasterType == TCacheResource::RGBM32) { + result = TRaster32P(latticeStep, latticeStep); + img = TRasterImageP(result); + } else if (rasterType == TCacheResource::RGBM64) { + result = TRaster64P(latticeStep, latticeStep); + img = TRasterImageP(result); + } else if (rasterType == TCacheResource::CM32) { + result = TRasterCM32P(latticeStep, latticeStep); + img = TToonzImageP(result, result->getBounds()); + } + + TImageCache::instance()->add(cacheId, img); + ++m_cellsCount; + + //DIAGNOSTICS_GLOADD("crCellsCnt", 1); + + return result; +} + +//---------------------------------------------------------------- + +bool TCacheResource::canDownloadSome(const TRect &rect) const +{ + return m_region.intersects(toQRect(rect)); +} + +//---------------------------------------------------------------- + +bool TCacheResource::canDownloadAll(const TRect &rect) const +{ + return contains(m_region, rect); +} + +//---------------------------------------------------------------- + +bool TCacheResource::canUpload(const TTile &tile) const +{ + int tileType; + return checkTile(tile) && checkRasterType(tile.getRaster(), tileType); +} + +//---------------------------------------------------------------- + +//! Returns true if the passed tile is compatible with the complex, and some +//! part of it is downloadable. +bool TCacheResource::canDownloadSome(const TTile &tile) const +{ + return checkTile(tile) && m_region.intersects(toQRect(getTileRect(tile))); +} + +//---------------------------------------------------------------- + +//! Returns true if the passed tile is compatible, and it can be downloaded +//! entirely. +bool TCacheResource::canDownloadAll(const TTile &tile) const +{ + return checkTile(tile) && contains(m_region, getTileRect(tile)); +} + +//---------------------------------------------------------------- + +//! Copies the passed tile in the tile complex. The passed tile \b must +//! possess integer geometry (ie tile.m_pos must have integer coordinates), +//! otherwise this function is a no-op. +bool TCacheResource::upload(const TPoint &pos, TRasterP ras) +{ + int tileType; + if (!checkRasterType(ras, tileType)) + return false; + + if (m_tileType == NONE) + m_tileType = tileType; + + //For all cells of the lattice which intersect the tile, upload the content in the + //complex + TRect tileRect(ras->getBounds() + pos); + TPoint initialPos(getCellPos(tileRect.getP00())); + + //DIAGNOSTICS_NUMBEREDSTRSET(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "upload", ::traduce(TRect(pos, ras->getSize()))); + + TPoint currPos; + for (currPos.x = initialPos.x; currPos.x <= tileRect.x1; currPos.x += latticeStep) + for (currPos.y = initialPos.y; currPos.y <= tileRect.y1; currPos.y += latticeStep) { + //Copy tile's content into the cell's raster. + TRect cellRect(currPos, TDimension(latticeStep, latticeStep)); + + TRect overlapRect(tileRect * cellRect); + assert(!overlapRect.isEmpty()); + + PointLess cellIndex(getCellIndex(currPos)); + std::pair cellInfos(touch(cellIndex)); + TRasterP cellRas(cellInfos.first); + + TRect temp(overlapRect - currPos); + TRasterP overlappingCellRas(cellRas->extract(temp)); + temp = TRect(overlapRect - tileRect.getP00()); + TRasterP overlappingTileRas(ras->extract(temp)); + + assert(overlappingCellRas->getBounds() == overlappingTileRas->getBounds()); + TRop::copy(overlappingCellRas, overlappingTileRas); + + cellInfos.second->m_modified = true; + } + + //Update the complex's content region + m_region += toQRect(tileRect); + + return true; +} + +//---------------------------------------------------------------- + +bool TCacheResource::upload(const TTile &tile) +{ + if (!checkTile(tile)) + return false; + + return upload(TPoint(tile.m_pos.x, tile.m_pos.y), tile.getRaster()); +} + +//---------------------------------------------------------------- + +//! Fills the passed tile with the data contained in the complex, returning +//! the copied region. +//! The same restriction of the upload() method applies here. +QRegion TCacheResource::download(const TPoint &pos, TRasterP ras) +{ + int tileType; + if (!checkRasterType(ras, tileType)) + return QRegion(); + + //Build the tile's rect + TRect tileRect(ras->getBounds() + pos); + + if (!m_region.intersects(toQRect(tileRect))) + return QRegion(); + + //For all cells intersecting the tile's rect, copy all those intersecting the + //complex's content region. + TPoint initialPos(getCellPos(tileRect.getP00())); + + TPoint currPos; + for (currPos.x = initialPos.x; currPos.x <= tileRect.x1; currPos.x += latticeStep) + for (currPos.y = initialPos.y; currPos.y <= tileRect.y1; currPos.y += latticeStep) { + TRect cellRect(currPos, TDimension(latticeStep, latticeStep)); + + TRect overlapRect(tileRect * cellRect); + assert(!overlapRect.isEmpty()); + QRect overlapQRect(toQRect(overlapRect)); + + if (m_region.intersects(overlapQRect)) { + //Extract the associated rasters and perform the copy to the input tile. + std::pair cellInfos(touch(getCellIndex(currPos))); + TRasterP cellRas(cellInfos.first); + + TRect temp(overlapRect - currPos); + TRasterP overlappingCellRas(cellRas->extract(temp)); + temp = TRect(overlapRect - tileRect.getP00()); + TRasterP overlappingTileRas(ras->extract(temp)); + + TRop::copy(overlappingTileRas, overlappingCellRas); + } + } + + return m_region.intersected(QRegion(toQRect(tileRect))); +} + +//---------------------------------------------------------------- + +QRegion TCacheResource::download(TTile &tile) +{ + if (!checkTile(tile)) + return QRegion(); + + return download(TPoint(tile.m_pos.x, tile.m_pos.y), tile.getRaster()); +} + +//---------------------------------------------------------------- + +bool TCacheResource::downloadAll(const TPoint &pos, TRasterP ras) +{ + int tileType; + if (!checkRasterType(ras, tileType)) + return false; + + //Build the tile's rect + TRect tileRect(ras->getBounds() + pos); + + if (!contains(m_region, tileRect)) + return false; + + //DIAGNOSTICS_NUMBEREDSTRSET(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "downloadAll", ::traduce(TRect(pos, ras->getSize()))); + + //For all cells intersecting the tile's rect, copy all those intersecting the + //complex's content region. + TPoint initialPos(getCellPos(tileRect.getP00())); + + TPoint currPos; + for (currPos.x = initialPos.x; currPos.x <= tileRect.x1; currPos.x += latticeStep) + for (currPos.y = initialPos.y; currPos.y <= tileRect.y1; currPos.y += latticeStep) { + TRect cellRect(currPos, TDimension(latticeStep, latticeStep)); + + TRect overlapRect(tileRect * cellRect); + assert(!overlapRect.isEmpty()); + QRect overlapQRect(toQRect(overlapRect)); + + if (m_region.intersects(overlapQRect)) { + //Extract the associated rasters and perform the copy to the input tile. + std::pair cellInfos(touch(getCellIndex(currPos))); + TRasterP cellRas(cellInfos.first); + + TRect temp(overlapRect - currPos); + TRasterP overlappingCellRas(cellRas->extract(temp)); + temp = TRect(overlapRect - tileRect.getP00()); + TRasterP overlappingTileRas(ras->extract(temp)); + + TRop::copy(overlappingTileRas, overlappingCellRas); + } + } + + return true; +} + +//---------------------------------------------------------------- + +bool TCacheResource::downloadAll(TTile &tile) +{ + if (!checkTile(tile)) + return false; + + return downloadAll(TPoint(tile.m_pos.x, tile.m_pos.y), tile.getRaster()); +} + +//----------------------------------------------------------------- + +//! Clears the complex on the specified region. Please observe that the actually cleared region +//! consists of all lattice cells intersecting the passed region, therefore resulting in a cleared region +//! typically larger than passed one, up to the lattice granularity. +void TCacheResource::clear(QRegion region) +{ + if (!m_region.intersects(region)) + return; + + //Get the region bbox + TRect bbox(toTRect(region.boundingRect())); + + //For all cells intersecting the bbox + TPoint initialPos(getCellPos(bbox.getP00())); + TPoint pos; + for (pos.x = initialPos.x; pos.x <= bbox.x1; pos.x += latticeStep) + for (pos.y = initialPos.y; pos.y <= bbox.y1; pos.y += latticeStep) { + QRect cellQRect(toQRect(TRect(pos, TDimension(latticeStep, latticeStep)))); + + if (region.intersects(cellQRect) && m_region.intersects(cellQRect)) { + //Release the associated cell from cache and clear the cell from the content region. + TImageCache::instance()->remove(getCellCacheId(pos)); + m_region -= cellQRect; + + --m_cellsCount; + + //DIAGNOSTICS_GLOADD("crCellsCnt", -1); + + //Release the cell from m_cellDatas + m_cellDatas[getCellIndex(pos)].m_modified = true; + } + } + + if (m_region.isEmpty()) { + m_tileType = NONE; + m_locksCount = 0; + } +} + +//**************************************************************************************************** +// Palette management +//**************************************************************************************************** + +bool TCacheResource::uploadPalette(TPaletteP palette) +{ + if (m_tileType == NONE) + m_tileType = CM32; + + if (m_tileType != CM32) { + assert(!"The resource already holds a non-colormap content!"); + return false; + } + + m_palette = palette; + return true; +} + +//----------------------------------------------------------------- + +void TCacheResource::downloadPalette(TPaletteP &palette) +{ + palette = m_palette; +} + +//**************************************************************************************************** +// References management +//**************************************************************************************************** + +void TCacheResource::addRef2(const TRect &rect) +{ + //DIAGNOSTICS_NUMBEREDSTRSET(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "addRef", ::traduce(rect)); + + //Add a reference to all cells intersecting the passed one + TPoint initialPos(getCellPos(rect.getP00())); + TPoint pos; + for (pos.x = initialPos.x; pos.x <= rect.x1; pos.x += latticeStep) + for (pos.y = initialPos.y; pos.y <= rect.y1; pos.y += latticeStep) { + PointLess cellIndex(getCellIndex(pos)); + CellData &cellData = m_cellDatas[cellIndex]; + cellData.m_referenced = true; + cellData.m_refsCount++; + } +} + +//----------------------------------------------------------------- + +void TCacheResource::release2(const TRect &rect) +{ + //DIAGNOSTICS_NUMBEREDSTRSET(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "release", ::traduce(rect)); + + if (m_locksCount > 0) + return; + + std::map::iterator it; + for (it = m_cellDatas.begin(); it != m_cellDatas.end();) { + if (!it->second.m_referenced) { + ++it; + continue; + } + + TPoint cellPos(getCellPos(it->first)); + TRect cellRect(cellPos, TDimension(latticeStep, latticeStep)); + + if (isEmpty(cellRect * rect)) { + ++it; + continue; + } + + QRect cellQRect(toQRect(cellRect)); + if (--it->second.m_refsCount <= 0) { + releaseCell(cellQRect, it->first, it->second.m_modified); + std::map::iterator jt = it++; + m_cellDatas.erase(jt); + } else + ++it; + } +} + +//----------------------------------------------------------------- + +void TCacheResource::addLock() +{ + //DIAGNOSTICS_NUMBEREDSTR(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "addLock"); + + ++m_locksCount; +} + +//----------------------------------------------------------------- + +void TCacheResource::releaseLock() +{ + //DIAGNOSTICS_NUMBEREDSTR(prefix + QString::number((UINT) this) + " | Stack | ", + //"crStack", "releaseLock"); + + m_locksCount = tmax(m_locksCount - 1, 0); + + if (m_locksCount > 0) + return; + + //Check for released cells + std::map::iterator it; + for (it = m_cellDatas.begin(); it != m_cellDatas.end();) + if (it->second.m_referenced) { + TPoint cellPos(getCellPos(it->first)); + QRect cellQRect(cellPos.x, cellPos.y, latticeStep, latticeStep); + + releaseCell(cellQRect, it->first, it->second.m_modified); + std::map::iterator jt = it++; + m_cellDatas.erase(jt); + } else + ++it; +} + +//----------------------------------------------------------------- + +void TCacheResource::releaseCell(const QRect &cellQRect, const PointLess &cellIndex, bool doSave) +{ + if (m_region.intersects(cellQRect)) { + std::string cellCacheId(getCellCacheId(cellIndex.x, cellIndex.y)); + + if (!(doSave && save(cellIndex))) + m_region -= cellQRect; + + TImageCache::instance()->remove(cellCacheId); + --m_cellsCount; + + //DIAGNOSTICS_GLOADD("crCellsCnt", -1); + } +} + +//----------------------------------------------------------------- + +//! Returns the current size, in MB, of the cache resource. +int TCacheResource::size() const +{ + //NOTE: It's better to store the size incrementally. This complies + //with the possibility of specifying a bbox to fit the stored cells to... + + return m_tileType == NONE ? 0 : m_tileType == RGBM64 ? (m_cellsCount << 11) : (m_cellsCount << 10); +} + +//**************************************************************************************************** +// Hard disk backing procedures +//**************************************************************************************************** + +void TCacheResource::enableBackup() +{ + if (m_backEnabled) + return; + TCacheResourcePool::instance()->startBacking(this); +} + +//----------------------------------------------------------------- + +void TCacheResource::invalidate() +{ + m_invalidated = true; +} + +//----------------------------------------------------------------- + +void TCacheResource::setPath(const TFilePath &path) +{ + m_path = path; +} + +//----------------------------------------------------------------- + +const TFilePath &TCacheResource::getPath() const +{ + return m_path; +} + +//----------------------------------------------------------------- + +bool TCacheResource::save(const PointLess &cellIndex, TRasterP cellRas) const +{ + if (!m_backEnabled || m_invalidated) + return false; + + assert(!m_path.isEmpty()); + + if (!cellRas) + cellRas = getRaster(TImageCache::instance()->get( + getCellCacheId(cellIndex.x, cellIndex.y), false)); + + assert(m_tileType != NONE); + + TFilePath fp(TCacheResourcePool::instance()->getPath() + m_path + getCellName(cellIndex.x, cellIndex.y)); + + if (m_tileType == CM32) { + ::saveCompressed(fp, cellRas); + } else { + TImageWriter::save(fp.withType(".tif"), cellRas); + } + + return true; +} + +//----------------------------------------------------------------- + +TRasterP TCacheResource::load(const PointLess &cellPos) +{ + if (m_path.isEmpty()) + return 0; + + TFilePath cellPath(TCacheResourcePool::instance()->getPath() + m_path + TFilePath(getCellName(cellPos.x, cellPos.y))); + TRasterP ras; + if (m_tileType == CM32) { + ::loadCompressed(cellPath, ras, CM32); + } else { + TImageReader::load(cellPath.withType(".tif"), ras); + } + + return ras; +} + +//----------------------------------------------------------------- + +std::pair TCacheResource::touch(const PointLess &cellIndex) +{ + std::string cellId(getCellCacheId(cellIndex.x, cellIndex.y)); + + std::map::iterator it = m_cellDatas.find(cellIndex); + if (it != m_cellDatas.end()) { + //Retrieve the raster from image cache + TImageP img(TImageCache::instance()->get(cellId, true)); + if (img) + return std::make_pair(getRaster(img), &it->second); + } + + it = m_cellDatas.insert(std::make_pair(cellIndex, CellData())).first; + + //Then, attempt retrieval from back resource + TRasterP ras(load(cellIndex)); + if (ras) { + TImageCache::instance()->add(cellId, TRasterImageP(ras)); + return std::make_pair(ras, &it->second); + } + + //Else, create it + return std::make_pair( + createCellRaster(m_tileType, cellId), //increases m_cellsCount too + &it->second); +} + +//----------------------------------------------------------------- + +void TCacheResource::save() +{ + if (m_backEnabled && !m_invalidated) { + assert(!m_path.isEmpty()); + + //Save each modified cell raster + std::map::iterator it; + for (it = m_cellDatas.begin(); it != m_cellDatas.end(); ++it) { + if (it->second.m_modified) + save(it->first); + } + + //Save the palette, if any + //SHOULD BE MOVED TO THE CACHERESOURCEPOOL!! + /*if(m_palette) + { + TFilePath fp(TCacheResourcePool::instance()->getPath() + m_path); + TOStream oss(fp); + + m_palette->saveData(oss); + }*/ + } +} + +//----------------------------------------------------------------- + +void TCacheResource::save(const TFilePath &fp) +{ + assert(!fp.isEmpty()); + + std::map::iterator it; + for (it = m_cellDatas.begin(); it != m_cellDatas.end(); ++it) { + TRasterP cellRas = getRaster(TImageCache::instance()->get( + getCellCacheId(it->first.x, it->first.y), false)); + + assert(m_tileType != NONE); + + TFilePath cellFp(fp + TFilePath(getCellName(it->first.x, it->first.y))); + + if (m_tileType == CM32) + ::saveCompressed(cellFp, cellRas); + else + TImageWriter::save(cellFp.withType(".tif"), cellRas); + } +} + +//----------------------------------------------------------------- + +void TCacheResource::clear() +{ + std::map::iterator it; + for (it = m_cellDatas.begin(); it != m_cellDatas.end(); ++it) { + std::string cellCacheId(getCellCacheId(it->first.x, it->first.y)); + TImageCache::instance()->remove(cellCacheId); + } + + m_cellDatas.clear(); +} + +/* +//**************************************************************************************************** +// Disk reference +//**************************************************************************************************** + +#include + +TCacheResource::DiskReference::DiskReference(const TFilePath& fp) +{ + QSettings settings( + QString::fromStdWString((TCacheResourcePool::instance()->getPath() + m_path + "resource.ini").getWideString()), + QSettings::IniFormat); + + settings.setValue("MemReference", 1); +} + +//----------------------------------------------------------------- + +TCacheResource::DiskReference::~DiskReference() +{ + QSettings settings( + QString::fromStdWString((TCacheResourcePool::instance()->getPath() + m_path + "resource.ini").getWideString()), + QSettings::IniFormat); + + int diskReference = settings.value("DiskReference").toInt(); + if(diskReference == 0) + TCacheResourcePool::instance()->clearResource(QString::fromStdWString(m_path.getWideString())); +} +*/ diff --git a/toonz/sources/common/tfx/tcacheresourcepool.cpp b/toonz/sources/common/tfx/tcacheresourcepool.cpp new file mode 100644 index 0000000..03f16a7 --- /dev/null +++ b/toonz/sources/common/tfx/tcacheresourcepool.cpp @@ -0,0 +1,295 @@ + + +//Qt includes +#include +#include +#include +#include +#include +#include + +//#define USE_SQLITE_HDPOOL + +#ifdef USE_SQLITE_HDPOOL +//SQLite include +#include "sqlite/sqlite3.h" +#endif + +#include "tcacheresourcepool.h" + +//Debug +//#define DIAGNOSTICS +//#include "diagnostics.h" + +//****************************************************************************************** +// Cache Resource Pool BACKED ON DISK +//****************************************************************************************** + +//STILL UNDER DEVELOPMENT... +class THDCacheResourcePool +{ +public: + THDCacheResourcePool() {} + ~THDCacheResourcePool() {} +}; + +//************************************************************************************* +// Cache resource pool methods involved with HD Pool management +//************************************************************************************* + +inline bool TCacheResourcePool::isHDActive() +{ +#ifdef USE_SQLITE_HDPOOL + return m_hdPool && m_hdPool->isActive(); +#else + return false; +#endif +} + +//----------------------------------------------------------------------------------- + +void TCacheResourcePool::reset() +{ + setPath("", "", ""); +} + +//---------------------------------------------------------------------- + +// Prevents the resources in memory from backing to disk. Observe that +// the actual content of the resource is NOT invalidated - since resources +// are intended as 'reference-protected' material which is expected to last +// as long as references are held. +void TCacheResourcePool::invalidateAll() +{ + QMutexLocker locker(&m_memMutex); + + MemResources::iterator it; + for (it = m_memResources.begin(); it != m_memResources.end(); ++it) + it->second->invalidate(); +} + +//---------------------------------------------------------------------- + +inline QString TCacheResourcePool::getPoolRoot(QString cacheRoot, QString projectName, QString sceneName) +{ + return QString(cacheRoot + "/render/" + projectName + "/" + sceneName + "/"); +} + +//---------------------------------------------------------------------- + +//! Connects to the pool associated to the given project/scene pair. +//! \warning As this closes the current connection before opening a new one, +//! make sure that no pool access happens at this point. You should also +//! verify that no resource from the old pair still exists. +void TCacheResourcePool::setPath(QString cacheRoot, QString projectName, QString sceneName) +{ + //There should be no resource in memory. + assert(m_memResources.empty()); + + //However, just in case, invalidate all resources so that no more resource backing + //operation take place for current resources, from now on. + //No care is paid as to whether active transactions currently exist. You + //have been warned by the way.... + invalidateAll(); + + delete m_hdPool; + m_hdPool = 0; + m_path = TFilePath(); + +#ifdef USE_SQLITE_HDPOOL + + if (!(cacheRoot.isEmpty() || projectName.isEmpty() || sceneName.isEmpty())) { + QString hdPoolRoot(getPoolRoot(cacheRoot, projectName, sceneName)); + m_hdPool = new THDCacheResourcePool(hdPoolRoot); + m_path = m_hdPool->getResourcesFilePath(); + } + +#endif +} + +//----------------------------------------------------------------------------------- + +void TCacheResourcePool::startBacking(TCacheResource *resource) +{ + assert(isHDActive()); + if (!isHDActive()) + return; + +#ifdef USE_SQLITE_HDPOOL + + resource->m_backEnabled = true; + + m_hdPool->buildBackingPath(resource); + +#endif +} + +//****************************************************************************************** +// Cache resource pool implementation +//****************************************************************************************** + +TCacheResourcePool *TCacheResourcePool::instance() +{ + static TCacheResourcePool theInstance; + return &theInstance; +} + +//---------------------------------------------------------------------- + +TCacheResourcePool::TCacheResourcePool() + : m_memMutex(QMutex::Recursive), m_searchCount(0), m_foundIterator(false), m_searchIterator(m_memResources.end()), m_hdPool(0), m_path() +{ + //Open the settings for cache retrieval +} + +//---------------------------------------------------------------------- + +TCacheResourcePool::~TCacheResourcePool() +{ + //Temporary + //performAutomaticCleanup(); + + delete m_hdPool; +} + +//---------------------------------------------------------------------- + +const TFilePath &TCacheResourcePool::getPath() const +{ + return m_path; +} + +//---------------------------------------------------------------------- + +//! Initializes an optimized search on the pool for a specific resource, caching successive +//! results. +//! \note Pool searches are serialized, and calls to this method lock the pool's mutex until a +//! corresponding number of endCachedSearch() methods are invoked. +void TCacheResourcePool::beginCachedSearch() +{ + m_memMutex.lock(); + m_searchCount++; +} + +//---------------------------------------------------------------------- + +//! The inverse to beginCachedSearch(). This method \b MUST be called in correspondence to +//! beginCachedSearch() calls. +void TCacheResourcePool::endCachedSearch() +{ + if (--m_searchCount <= 0) { + m_foundIterator = false; + m_searchIterator = m_memResources.end(); + } + m_memMutex.unlock(); +} + +//---------------------------------------------------------------------- + +//! Attempts retrieval of the resource with specified name, and eventually creates it if +//! the createIfNone parameter is set. +TCacheResource *TCacheResourcePool::getResource(const std::string &name, bool createIfNone) +{ + //DIAGNOSTICS_TIMER("#times.txt | getResource Overall time"); + //DIAGNOSTICS_MEANTIMER("#times.txt | getResource Mean time"); + + TCacheResource *result = 0; + + //NOTA: Passa ad un oggetto lockatore. Quello e' in grado di gestire i casi di eccezioni ecc.. + beginCachedSearch(); + + //Search for an already allocated resource + if (m_searchIterator == m_memResources.end()) { + m_searchIterator = m_memResources.lower_bound(name); + if (m_searchIterator != m_memResources.end()) + if (!(name < m_searchIterator->first)) + m_foundIterator = true; + else if (m_searchIterator != m_memResources.begin()) + m_searchIterator--; + } + + if (m_foundIterator) { + result = m_searchIterator->second; + + endCachedSearch(); + return result; + } + + { + QString resourcePath; + QString resourceFlags; + + if (isHDActive()) { +#ifdef USE_SQLITE_HDPOOL + + //DIAGNOSTICS_TIMER("#times.txt | HDPOOL getResource Overall time"); + //DIAGNOSTICS_MEANTIMER("#times.txt | HDPOOL getResource Mean time"); + + //Search in the HD pool + ReadQuery query(m_hdPool); + + bool ret = query.prepare( + "SELECT Path, Flags FROM Resources WHERE Name = '" + QString::fromStdString(name) + "';"); + + //If an error occurred, assume the resource does not exist. Doing nothing works fine. + assert(ret); + + if (query.step()) { + resourcePath = query.value(0); + resourceFlags = query.value(1); + } + +#endif + } + + if (!resourcePath.isEmpty() || createIfNone) { + TCacheResource *result = new TCacheResource; + result->m_pos = m_searchIterator = + m_memResources.insert(m_searchIterator, std::make_pair(name, result)); + +//DIAGNOSTICS_STRSET("#resources.txt | RISORSE | " + QString::number((UINT) result) + " | Name", +//QString::fromStdString(name).left(70)); + +#ifdef USE_SQLITE_HDPOOL + if (isHDActive()) + m_hdPool->loadResourceInfos(result, resourcePath); +#endif + + m_foundIterator = true; + endCachedSearch(); + + return result; + } + } + + endCachedSearch(); + return 0; +} + +//---------------------------------------------------------------------- + +void TCacheResourcePool::releaseResource(TCacheResource *resource) +{ + QMutexLocker locker(&m_memMutex); + + //Re-check the resource's reference count. This is necessary since a concurrent + //thread may have locked the memMutex for resource retrieval BEFORE this one. + //If that is the case, the resource's refCount has increased back above 0. + if (resource->m_refCount > 0) + return; + +#ifdef USE_SQLITE_HDPOOL + QMutexLocker flushLocker(isHDActive() ? &m_hdPool->m_flushMutex : 0); + + if (isHDActive()) { + //Flush all resource updates as this resource is being destroyed + m_hdPool->flushResources(); + + //Save the resource infos + m_hdPool->saveResourceInfos(resource); + } +#endif + + m_memResources.erase(resource->m_pos); + delete resource; +} diff --git a/toonz/sources/common/tfx/tfx.cpp b/toonz/sources/common/tfx/tfx.cpp new file mode 100644 index 0000000..02cfbea --- /dev/null +++ b/toonz/sources/common/tfx/tfx.cpp @@ -0,0 +1,1176 @@ + + +// TnzCore includes +#include "tconst.h" +#include "tutil.h" +#include "tstream.h" + +// TnzBase includes +#include "tparamcontainer.h" +#include "tfxattributes.h" +#include "texternfx.h" +#include "tpassivecachemanager.h" + +// STD includes +#include + +#include "tfx.h" + +//============================================================================== + +TIStream &operator>>(TIStream &in, TFxP &p) +{ + TPersist *tmp = 0; + in >> tmp; + p = TFxP(dynamic_cast(tmp)); + return in; +} + +//============================================================================== +// +// namespace {} +// +//------------------------------------------------------------------------------ + +namespace +{ + +//------------------------------------------------------------------------------ + +typedef pair NamePort; +typedef map PortTable; +typedef vector PortArray; + +//------------------------------------------------------------------------------ + +class PortNameEq +{ + string m_name; + +public: + PortNameEq(const string &name) : m_name(name) {} + ~PortNameEq() {} + bool operator()(const NamePort &np) { return np.first == m_name; } +}; + +//------------------------------------------------------------------------------ + +void skipChild(TIStream &is) +{ + while (!is.eos()) { + string dummy = is.getString(); + string tagName; + while (is.openChild(tagName)) { + skipChild(is); + if (is.isBeginEndTag()) + is.matchTag(tagName); + is.closeChild(); + } + } +} + +//------------------------------------------------------------------------------ + +TPointD updateDagPosition(const TPointD &pos, const VersionNumber &tnzVersion) +{ + if (tnzVersion < VersionNumber(1, 16)) + return TConst::nowhere; + return pos; +} + +} // namespace + +//============================================================================== +// +// TFxParamChange +// +//------------------------------------------------------------------------------ + +TFxParamChange::TFxParamChange(TFx *fx, double firstAffectedFrame, double lastAffectedFrame, bool dragging) + : TFxChange(fx, firstAffectedFrame, lastAffectedFrame, dragging) +{ +} + +//-------------------------------------------------- + +TFxParamChange::TFxParamChange(TFx *fx, const TParamChange &src) + : TFxChange(fx, src.m_firstAffectedFrame, src.m_lastAffectedFrame, src.m_dragging) +{ +} + +//============================================================================== +// +// TFxChange +// +//------------------------------------------------------------------------------ + +double TFxChange::m_minFrame = -(std::numeric_limits::max)(); +double TFxChange::m_maxFrame = +(std::numeric_limits::max)(); + +//============================================================================== +// +// TFxPortDynamicGroup +// +//------------------------------------------------------------------------------ + +TFxPortDynamicGroup::TFxPortDynamicGroup(const std::string &prefix, int minSize) + : m_portsPrefix(prefix), m_minPortsCount(minSize) +{ +} + +//-------------------------------------------------- + +TFxPortDynamicGroup::~TFxPortDynamicGroup() +{ + clear(); +} + +//-------------------------------------------------- + +void TFxPortDynamicGroup::addPort(TFxPort *port) +{ + m_ports.push_back(port); +} + +//-------------------------------------------------- + +void TFxPortDynamicGroup::removePort(TFxPort *port) +{ + m_ports.resize(std::remove(m_ports.begin(), m_ports.end(), port) - m_ports.begin()); + delete port; +} + +//-------------------------------------------------- + +void TFxPortDynamicGroup::clear() +{ + std::for_each(m_ports.begin(), m_ports.end(), TDeleteObjectFunctor()); + m_ports.clear(); +} + +//============================================================================== +// +// TFxImp +// +//------------------------------------------------------------------------------ + +class TFxImp +{ +public: + TFx *m_fx; //!< Fx back-pointer + TFxImp *m_prev, *m_next; //!< Linked fxs + + wstring m_name; + wstring m_fxId; + + PortTable m_portTable; //!< Name -> port map + PortArray m_portArray; //!< Ports container + + TParamContainer m_paramContainer; + + std::set m_outputPort; + TFxTimeRegion m_activeTimeRegion; + std::set m_observers; + + TFxAttributes m_attributes; + + static unsigned long m_nextId; + unsigned long m_id; //!< Unique fx identifier, per Toonz session. + //!< It is intended to be used \b solely to build + //!< an internal string description of the fx. +public: + TFxImp(TFx *fx) + : m_fx(fx), m_activeTimeRegion(TFxTimeRegion::createUnlimited()), m_id() + { + m_prev = m_next = this; + } + + ~TFxImp() + { + m_prev->m_next = m_next; + m_next->m_prev = m_prev; + } + + bool checkLinks() const + { + assert(m_prev); + assert(m_next); + assert(m_prev->m_next == this); + assert(m_next->m_prev == this); + + return true; + } + +private: + // not copyable + TFxImp(const TFxImp &); + TFxImp &operator=(const TFxImp &); +}; + +//-------------------------------------------------- + +unsigned long TFxImp::m_nextId = 0; + +//============================================================================== +// +// TFxFactory +// +//------------------------------------------------------------------------------ + +class TFxFactory // singleton +{ + typedef std::map> Table; + + Table m_table; + std::vector m_map; + + TFxFactory() {} + +public: + static TFxFactory *instance() + { + static TFxFactory _instance; + return &_instance; + } + + void add(const TFxInfo &info, TFxDeclaration *decl) + { + // check for dups ??? + std::pair p(info, decl); + m_table[info.m_name] = p; + } + + TFx *create(std::string name) + { + Table::iterator it = m_table.find(name); + if (it != m_table.end()) { + TFxDeclaration *decl = it->second.second; + + TPersist *obj = decl->create(); + assert(obj); + + return dynamic_cast(obj); + } else + return TExternFx::create(name); + } + + void getFxInfos(std::vector &info) const + { + for (Table::const_iterator it = m_table.begin(); it != m_table.end(); ++it) + info.push_back(it->second.first); + } + + TFxInfo getFxInfo(const std::string &fxIdentifier) const + { + Table::const_iterator it = m_table.find(fxIdentifier); + return (it != m_table.end()) ? it->second.first : TFxInfo(); + } +}; + +//============================================================================== +// +// TFxDeclaration +// +//------------------------------------------------------------------------------ + +TFxDeclaration::TFxDeclaration(const TFxInfo &info) + : TPersistDeclaration(info.m_name) +{ + TFxFactory::instance()->add(info, this); +} + +//============================================================================== +// +// TFx +// +//------------------------------------------------------------------------------ + +DEFINE_CLASS_CODE(TFx, 3) + +//------------------------------------------------------------------------------ + +TFx::TFx() + : TSmartObject(m_classCode) // TPersist(TFxImp::m_instances) + , + m_imp(new TFxImp(this)) +{ +} + +//-------------------------------------------------- + +TFx::~TFx() +{ + for (std::set::iterator it = m_imp->m_outputPort.begin(); + it != m_imp->m_outputPort.end(); ++it) { + TFxPort *port = *it; + port->setFx(0); + } + + delete m_imp; +} + +//-------------------------------------------------- + +wstring TFx::getName() const +{ + return m_imp->m_name; +} + +//-------------------------------------------------- + +void TFx::setName(wstring name) +{ + m_imp->m_name = name; +} + +//-------------------------------------------------- + +wstring TFx::getFxId() const +{ + return m_imp->m_fxId; +} + +//-------------------------------------------------- + +void TFx::setFxId(wstring id) +{ + m_imp->m_fxId = id; +} + +//-------------------------------------------------- + +TFxAttributes *TFx::getAttributes() const +{ + return &m_imp->m_attributes; +} + +//-------------------------------------------------- + +const TParamContainer *TFx::getParams() const +{ + return &m_imp->m_paramContainer; +} + +//-------------------------------------------------- + +TParamContainer *TFx::getParams() +{ + return &m_imp->m_paramContainer; +} + +//-------------------------------------------------- + +TFx *TFx::clone(bool recursive) const +{ + TFx *fx = TFx::create(getFxType()); + assert(fx); + return this->clone(fx, recursive); +} + +TFx *TFx::clone(TFx *fx, bool recursive) const +{ + // Copy misc stuff + + fx->m_imp->m_activeTimeRegion = m_imp->m_activeTimeRegion; + fx->setIdentifier(getIdentifier()); + fx->getParams()->copy(getParams()); + fx->setFxId(getFxId()); + fx->setName(getName()); + + // Copy attributes + *fx->getAttributes() = *getAttributes(); + + // Clone the dynamic ports pool + if (hasDynamicPortGroups()) { + int p, pCount = m_imp->m_portArray.size(); + for (p = 0; p != pCount; ++p) { + const NamePort &namedPort = m_imp->m_portArray[p]; + + int groupIdx = namedPort.second->getGroupIndex(); + if (groupIdx >= 0 && !fx->getInputPort(namedPort.first)) + fx->addInputPort(namedPort.first, new TRasterFxPort, groupIdx); + } + + assert(pCount == fx->getInputPortCount()); + } + + // copia ricorsiva sulle porte + if (recursive) { + int p, pCount = getInputPortCount(); + for (p = 0; p != pCount; ++p) { + TFxPort *port = getInputPort(p); + if (port->getFx()) + fx->connect(getInputPortName(p), port->getFx()->clone(true)); + } + } + + return fx; +} + +//-------------------------------------------------- + +void TFx::linkParams(TFx *fx) +{ + if (this == fx) + return; + getParams()->link(fx->getParams()); + m_imp->m_activeTimeRegion = fx->m_imp->m_activeTimeRegion; + + // aggiorno i link + assert(m_imp->checkLinks()); + assert(fx->m_imp->checkLinks()); + + tswap(m_imp->m_next, fx->m_imp->m_next); + tswap(m_imp->m_next->m_prev, fx->m_imp->m_next->m_prev); + + assert(m_imp->checkLinks()); + assert(fx->m_imp->checkLinks()); +} + +//-------------------------------------------------- + +void TFx::unlinkParams() +{ + // clone dei parametri + getParams()->unlink(); + + assert(m_imp->m_prev); + assert(m_imp->m_next); + assert(m_imp->m_prev->m_next == m_imp); + assert(m_imp->m_next->m_prev == m_imp); + m_imp->m_prev->m_next = m_imp->m_next; + m_imp->m_next->m_prev = m_imp->m_prev; + m_imp->m_next = m_imp->m_prev = m_imp; + + notify(TFxParamsUnlinked(this)); +} + +//-------------------------------------------------- + +bool TFx::addInputPort(const std::string &name, TFxPort &port) +{ + PortTable::iterator it = m_imp->m_portTable.find(name); + if (it != m_imp->m_portTable.end()) + return false; + + m_imp->m_portTable[name] = &port; + m_imp->m_portArray.push_back(NamePort(name, &port)); + port.setOwnerFx(this); // back pointer to the owner... + + return true; +} + +//-------------------------------------------------- + +bool TFx::addInputPort(const std::string &name, TFxPort *port, int groupIndex) +{ + if (!port) { + assert(port); + return false; + } + + if (groupIndex >= dynamicPortGroupsCount()) { + assert(groupIndex < dynamicPortGroupsCount()); + return false; + } + + if (!addInputPort(name, *port)) + return false; + + // Assign the port to the associated group + port->m_groupIdx = groupIndex; + + TFxPortDG *group = const_cast(dynamicPortGroup(groupIndex)); + group->addPort(port); + + assert(name.find(group->m_portsPrefix) == 0); + + return true; +} + +//-------------------------------------------------- + +bool TFx::removeInputPort(const std::string &name) +{ + m_imp->m_portTable.erase(name); + + PortArray::iterator it = std::find_if(m_imp->m_portArray.begin(), m_imp->m_portArray.end(), PortNameEq(name)); + if (it == m_imp->m_portArray.end()) + return false; + + TFxPort *port = it->second; + port->setOwnerFx(0); + + if (port->m_groupIdx >= 0) { + TFxPortDG *group = const_cast(this->dynamicPortGroup(port->m_groupIdx)); + group->removePort(port); // The port is DELETED here + } + + m_imp->m_portArray.erase(it); + return true; +} + +//-------------------------------------------------- + +namespace +{ +struct IsPrefix { + const std::string &m_prefix; + bool operator()(const NamePort &nameport) + { + return (strncmp(m_prefix.c_str(), nameport.first.c_str(), m_prefix.size()) == 0); + } +}; +} // namespace + +void TFx::clearDynamicPortGroup(int g) +{ + TFxPortDG *dg = const_cast(this->dynamicPortGroup(g)); + + const std::string &prefix = dg->portsPrefix(); + + std::string prefixEnd = prefix; + ++prefixEnd[prefixEnd.size() - 1]; + + { + // Delete all associated ports from the ports table + PortTable::iterator pBegin(m_imp->m_portTable.lower_bound(prefix)); + PortTable::iterator pEnd(m_imp->m_portTable.lower_bound(prefixEnd)); + + m_imp->m_portTable.erase(pBegin, pEnd); + + // Traverse the ports array and remove all ports in the group + IsPrefix func = {prefix}; + m_imp->m_portArray.resize(std::remove_if(m_imp->m_portArray.begin(), m_imp->m_portArray.end(), func) - m_imp->m_portArray.begin()); + } + + dg->clear(); // Has ports ownership, so deletes them +} + +//-------------------------------------------------- + +bool TFx::addOutputConnection(TFxPort *port) +{ + assert(port->getFx() == this); + return m_imp->m_outputPort.insert(port).second; +} + +//-------------------------------------------------- + +bool TFx::removeOutputConnection(TFxPort *port) +{ + std::set::iterator it = m_imp->m_outputPort.find(port); + if (it == m_imp->m_outputPort.end()) + return false; + + m_imp->m_outputPort.erase(it); + return true; +} + +//-------------------------------------------------- + +int TFx::getOutputConnectionCount() const +{ + return m_imp->m_outputPort.size(); +} + +//-------------------------------------------------- + +TFxPort *TFx::getOutputConnection(int i) const +{ + assert(0 <= i && i <= (int)m_imp->m_outputPort.size()); + std::set::iterator it = m_imp->m_outputPort.begin(); + std::advance(it, i); + if (it == m_imp->m_outputPort.end()) + return 0; + return *it; +} + +//-------------------------------------------------- + +bool TFx::disconnect(const std::string &name) +{ + TFxPort *port = getInputPort(name); + if (!port) + return false; + + port->setFx(0); + return true; +} + +//-------------------------------------------------- + +bool TFx::connect(const std::string &name, TFx *fx) +{ + TFxPort *port = getInputPort(name); + if (!port) + return false; + + port->setFx(fx); + return true; +} + +//-------------------------------------------------- + +int TFx::getInputPortCount() const +{ + return m_imp->m_portArray.size(); +} + +//-------------------------------------------------- + +TFxPort *TFx::getInputPort(int index) const +{ + assert(0 <= index && index < (int)m_imp->m_portArray.size()); + return m_imp->m_portArray[index].second; +} + +//-------------------------------------------------- + +string TFx::getInputPortName(int index) const +{ + assert(0 <= index && index < (int)(m_imp->m_portArray.size())); + return m_imp->m_portArray[index].first; +} + +//-------------------------------------------------- + +bool TFx::renamePort(const string &oldName, const string &newName) +{ + PortTable::iterator it = m_imp->m_portTable.find(oldName); + if (it == m_imp->m_portTable.end()) + return false; + + TFxPort *port = it->second; + m_imp->m_portTable.erase(it); + m_imp->m_portTable[newName] = port; + + PortArray::iterator it2; + for (it2 = m_imp->m_portArray.begin(); it2 != m_imp->m_portArray.end(); ++it2) { + if (it2->first != oldName) + continue; + + it2->first = newName; + break; + } + + return true; +} + +//-------------------------------------------------- + +TFxPort *TFx::getInputPort(const std::string &name) const +{ + PortTable::iterator it = m_imp->m_portTable.find(name); + if (it == m_imp->m_portTable.end()) + return 0; + + return m_imp->m_portTable[name]; +} + +//-------------------------------------------------- + +int TFx::getReferenceColumnIndex() const +{ + if (!m_imp->m_portArray.empty()) { + TFx *fx = m_imp->m_portArray[0].second->getFx(); + if (fx) + return fx->getReferenceColumnIndex(); + } + return -1; +} + +//-------------------------------------------------- + +TFxTimeRegion TFx::getTimeRegion() const +{ + if (m_imp->m_portTable.empty()) + return TFxTimeRegion::createUnlimited(); + + TFxTimeRegion tr((std::numeric_limits::max)(), -(std::numeric_limits::max)()); + + PortTable::iterator it = m_imp->m_portTable.begin(); + for (; it != m_imp->m_portTable.end(); ++it) { + TFxPort *port = it->second; + if (port && port->isConnected() && !port->isaControlPort()) { + TFx *fx = port->getFx(); + assert(fx); + tr += fx->getTimeRegion(); + } + } + + return tr; +} + +//-------------------------------------------------- + +void TFx::setActiveTimeRegion(const TFxTimeRegion &tr) +{ + m_imp->m_activeTimeRegion = tr; +} + +//-------------------------------------------------- + +TFxTimeRegion TFx::getActiveTimeRegion() const +{ + return m_imp->m_activeTimeRegion; +} + +//-------------------------------------------------- + +void TFx::onChange(const TParamChange &c) +{ + TFxParamChange change(this, c); + notify(change); +} + +//-------------------------------------------------- + +void TFx::addObserver(TFxObserver *obs) +{ + m_imp->m_observers.insert(obs); +} + +//-------------------------------------------------- + +void TFx::removeObserver(TFxObserver *obs) +{ + m_imp->m_observers.erase(obs); +} + +//-------------------------------------------------- + +void TFx::notify(const TFxChange &change) +{ + for (std::set::iterator it = m_imp->m_observers.begin(); + it != m_imp->m_observers.end(); ++it) + (*it)->onChange(change); +} + +//-------------------------------------------------- + +void TFx::notify(const TFxPortAdded &change) +{ + for (std::set::iterator it = m_imp->m_observers.begin(); + it != m_imp->m_observers.end(); ++it) + (*it)->onChange(change); +} + +//-------------------------------------------------- + +void TFx::notify(const TFxPortRemoved &change) +{ + for (std::set::iterator it = m_imp->m_observers.begin(); + it != m_imp->m_observers.end(); ++it) + (*it)->onChange(change); +} + +//-------------------------------------------------- + +void TFx::notify(const TFxParamAdded &change) +{ + for (std::set::iterator it = m_imp->m_observers.begin(); + it != m_imp->m_observers.end(); ++it) + (*it)->onChange(change); +} + +//-------------------------------------------------- + +void TFx::notify(const TFxParamRemoved &change) +{ + for (std::set::iterator it = m_imp->m_observers.begin(); + it != m_imp->m_observers.end(); ++it) + (*it)->onChange(change); +} + +//-------------------------------------------------- + +unsigned long TFx::getIdentifier() const +{ + return m_imp->m_id; +} + +//-------------------------------------------------- + +void TFx::setIdentifier(unsigned long id) +{ + m_imp->m_id = id; +} + +//-------------------------------------------------- + +void TFx::setNewIdentifier() +{ + m_imp->m_id = ++m_imp->m_nextId; +} + +//-------------------------------------------------- + +void TFx::loadData(TIStream &is) +{ + string tagName; + VersionNumber tnzVersion = is.getVersion(); + + QList groupIds; + QList groupNames; + while (is.openChild(tagName)) { + if (tagName == "params") { + while (!is.eos()) { + string paramName; + while (is.openChild(paramName)) { + TParamP param = getParams()->getParam(paramName); + if (param) + param->loadData(is); + else // il parametro non e' presente -> skip + skipChild(is); + + is.closeChild(); + } + } + } else if (tagName == "paramsLinkedTo") { + TPersist *p = 0; + is >> p; + TFx *fx = dynamic_cast(p); + if (fx == 0) + throw TException("Missing linkedSetRoot"); + linkParams(fx); + } else if (tagName == "ports") { + string portName; + while (!is.eos()) { + while (is.openChild(portName)) { + VersionNumber version = is.getVersion(); + compatibilityTranslatePort(version.first, version.second, portName); + + // Try to find the specified port + TFxPort *port = getInputPort(portName); + if (!port) { + // Scan dynamic port groups for a containing one - add a port there if found + int g, gCount = dynamicPortGroupsCount(); + for (g = 0; g != gCount; ++g) { + TFxPortDG *group = const_cast(dynamicPortGroup(g)); + + if (group->contains(portName)) { + addInputPort(portName, port = new TRasterFxPort, g); + break; + } + } + } + + // Could not find (or add) a port with the corresponding name - throw + if (!port) + throw TException(string("port '") + portName + "' is not present"); + + if (!is.eos()) { + TPersist *p = 0; + is >> p; + TFx *fx = dynamic_cast(p); + port->setFx(fx); + } + + is.closeChild(); + } + } + } else if (tagName == "dagNodePos") { + TPointD p; + is >> p.x >> p.y; + m_imp->m_attributes.setDagNodePos(updateDagPosition(p, tnzVersion)); + } else if (tagName == "numberId") { + int numberId = 0; + is >> numberId; + m_imp->m_attributes.setId(numberId); + } else if (tagName == "passiveCacheId") { + int passiveCacheId = 0; + is >> passiveCacheId; + + assert(passiveCacheId > 0); + TPassiveCacheManager::instance()->declareCached(this, passiveCacheId); + } else if (tagName == "name") { + // passo attraverso un filepath solo per evitare i problemi di blank + // o caratteri strani dentro il nome (sospetto che tfilepath sia gestito + // correttamente mentre wstring no + TFilePath tmp; + is >> tmp; + setName(tmp.getWideName()); + } else if (tagName == "fxId") { + TFilePath tmp; + is >> tmp; + setFxId(tmp.getWideName()); + } else if (tagName == "enabled") { + int flag = 1; + is >> flag; + m_imp->m_attributes.enable(flag != 0); + } else if (tagName == "opened") { + int opened = 1; + is >> opened; + m_imp->m_attributes.setIsOpened(opened); + } else if (tagName == "groupIds") { + int groupId; + while (!is.eos()) { + is >> groupId; + groupIds.append(groupId); + } + } else if (tagName == "groupNames") { + wstring groupName; + while (!is.eos()) { + is >> groupName; + groupNames.append(groupName); + } + } else { + throw TException("Unknown tag!"); + } + is.closeChild(); + } + if (!groupIds.isEmpty()) { + assert(groupIds.size() == groupNames.size()); + int i; + for (i = 0; i < groupIds.size(); i++) { + m_imp->m_attributes.setGroupId(groupIds[i]); + m_imp->m_attributes.setGroupName(groupNames[i]); + } + } +} + +//-------------------------------------------------- + +void TFx::saveData(TOStream &os) +{ + TFx *linkedSetRoot = this; + if (m_imp->m_next != m_imp) { + TFxImp *imp = m_imp->m_next; + int guard = 0; + while (guard++ < 1000 && imp != m_imp) { + if (imp->m_fx < linkedSetRoot) + linkedSetRoot = imp->m_fx; + imp = imp->m_next; + } + assert(imp == m_imp); + assert(linkedSetRoot); + } + if (linkedSetRoot == this) { + os.openChild("params"); + for (int i = 0; i < getParams()->getParamCount(); i++) { + string paramName = getParams()->getParamName(i); + TParam *param = getParams()->getParam(i); + os.openChild(paramName); + param->saveData(os); + os.closeChild(); + } + os.closeChild(); + } else { + os.openChild("paramsLinkedTo"); + os << linkedSetRoot; + os.closeChild(); + } + + os.openChild("ports"); + for (PortTable::iterator pit = m_imp->m_portTable.begin(); + pit != m_imp->m_portTable.end(); ++pit) { + os.openChild(pit->first); + if (pit->second->isConnected()) + os << TFxP(pit->second->getFx()).getPointer(); + os.closeChild(); + } + os.closeChild(); + + TPointD p = m_imp->m_attributes.getDagNodePos(); + if (p != TConst::nowhere) + os.child("dagNodePos") << p.x << p.y; + int numberId = m_imp->m_attributes.getId(); + os.child("numberId") << numberId; + bool cacheEnabled = TPassiveCacheManager::instance()->cacheEnabled(this); + if (cacheEnabled) + os.child("passiveCacheId") << TPassiveCacheManager::instance()->getPassiveCacheId(this); + wstring name = getName(); + if (name != L"") + os.child("name") << TFilePath(name); + wstring fxId = getFxId(); + os.child("fxId") << fxId; + if (!m_imp->m_attributes.isEnabled()) + os.child("enabled") << 0; + os.child("opened") << (int)m_imp->m_attributes.isOpened(); + if (m_imp->m_attributes.isGrouped()) { + os.openChild("groupIds"); + QStack groupIdStack = m_imp->m_attributes.getGroupIdStack(); + int i; + for (i = 0; i < groupIdStack.size(); i++) + os << groupIdStack[i]; + os.closeChild(); + os.openChild("groupNames"); + QStack groupNameStack = m_imp->m_attributes.getGroupNameStack(); + for (i = 0; i < groupNameStack.size(); i++) + os << groupNameStack[i]; + os.closeChild(); + } +} + +//-------------------------------------------------- + +void TFx::loadPreset(TIStream &is) +{ + string tagName; + while (is.openChild(tagName)) { + if (tagName == "dvpreset") { + string fxId = is.getTagAttribute("fxId"); + if (fxId != getFxType()) + throw TException("Preset doesn't match the fx type"); + } else if (tagName == "params") { + while (!is.eos()) { + string paramName; + while (is.openChild(paramName)) { + try { + TParamP param = getParams()->getParam(paramName); + param->loadData(is); + } catch (TException &) { /*skip*/ + } // il parametro non e' presente + is.closeChild(); + } + } + } else { + throw TException("Fx preset unknown tag!"); + } + } +} + +//-------------------------------------------------- + +void TFx::savePreset(TOStream &os) +{ + map attributes; + attributes.insert(std::make_pair(string("ver"), string("1.0"))); + attributes.insert(std::make_pair(string("fxId"), getFxType())); + + os.openChild("dvpreset", attributes); + + os.openChild("params"); + for (int i = 0; i < getParams()->getParamCount(); i++) { + string paramName = getParams()->getParamName(i); + TParam *param = getParams()->getParam(i); + os.openChild(paramName); + param->saveData(os); + os.closeChild(); + } + os.closeChild(); + + os.closeChild(); +} + +//-------------------------------------------------- + +void TFx::disconnectAll() +{ + int p, pCount = getInputPortCount(); + for (p = 0; p != pCount; ++p) + getInputPort(p)->setFx(0); +} + +//-------------------------------------------------- + +TFx *TFx::create(string name) +{ + return TFxFactory::instance()->create(name); +} + +//-------------------------------------------------- + +void TFx::listFxs(std::vector &info) +{ + TFxFactory::instance()->getFxInfos(info); +} + +//-------------------------------------------------- + +TFxInfo TFx::getFxInfo(const std::string &fxIdentifier) +{ + return TFxFactory::instance()->getFxInfo(fxIdentifier); +} + +//-------------------------------------------------- + +TFx *TFx::getLinkedFx() const +{ + assert(m_imp->m_next); + assert(m_imp->m_next->m_prev == m_imp); + assert(m_imp->m_next->m_fx != 0); + return m_imp->m_next->m_fx; +} + +//=================================================== +// +// TFxTimeRegion +// +//-------------------------------------------------- + +//! Creates an unlimited time region. +TFxTimeRegion::TFxTimeRegion() + : m_start((std::numeric_limits::max)()), m_end(-(std::numeric_limits::max)()) +{ +} + +//-------------------------------------------------- + +//! Creates a time region with specified start (included) and end (\b excluded). +TFxTimeRegion::TFxTimeRegion(double start, double end) + : m_start(start), m_end(end) +{ +} + +//-------------------------------------------------- + +TFxTimeRegion TFxTimeRegion::createUnlimited() +{ + return TFxTimeRegion( + -(std::numeric_limits::max)(), + (std::numeric_limits::max)()); +} + +//-------------------------------------------------- + +bool TFxTimeRegion::contains(double time) const +{ + return (m_start <= time && time < m_end); +} + +//-------------------------------------------------- + +bool TFxTimeRegion::isUnlimited() const +{ + return (m_start == -(std::numeric_limits::max)() || + m_end == (std::numeric_limits::max)()); +} + +//-------------------------------------------------- + +bool TFxTimeRegion::isEmpty() const +{ + return (m_end <= m_start); +} + +//-------------------------------------------------- + +bool TFxTimeRegion::getFrameCount(int &count) const +{ + if (isUnlimited()) + return false; + count = tfloor(m_end) - tceil(m_start); + return true; +} + +//-------------------------------------------------- + +int TFxTimeRegion::getFirstFrame() const +{ + return tceil(m_start); +} + +//-------------------------------------------------- + +int TFxTimeRegion::getLastFrame() const +{ + if (m_end == (std::numeric_limits::max)()) + return (std::numeric_limits::max)(); + else + return tceil(m_end) - 1; +} + +//=================================================== diff --git a/toonz/sources/common/tfx/tfxcachemanager.cpp b/toonz/sources/common/tfx/tfxcachemanager.cpp new file mode 100644 index 0000000..66ca963 --- /dev/null +++ b/toonz/sources/common/tfx/tfxcachemanager.cpp @@ -0,0 +1,848 @@ + + +#include + +#include "trenderer.h" +#include "tcacheresourcepool.h" + +#include "tfxcachemanager.h" + +//Debug +//#define DIAGNOSTICS +#ifdef DIAGNOSTICS + +#include "diagnostics.h" + +//#define WRITESTACK +//#define WRITESUBRECTS +//#define WRITEGENERAL + +namespace +{ +QString traduce(const TRectD &rect) +{ + return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + QString::number(rect.x1) + " " + QString::number(rect.y1) + "]"; +} + +QString traduce(const TTile &tile) +{ + TDimension dim(tile.getRaster()->getSize()); + TRectD tileRect(tile.m_pos, TDimensionD(dim.lx, dim.ly)); + return traduce(tileRect); +} + +QString prefixInfo("#info.txt | "); +QString prefixWarn("#warning.txt | "); +QString prefixErr("#error.txt | "); +QString prefixTest("#TestRun.txt | "); +QString prefixComp("#Computing.txt | "); +QString prefixSubTiles("#SubTiles.txt | "); +} + +#endif //DIAGNOSTICS + +//**************************************************************************************************** +// Explanation +//**************************************************************************************************** + +/* +This file contains most of the code that deals with smart caching during a Toonz render process. + +The paradigms on which the 'smart caching' takes place are: + + - Only calculations resulting in an image positioned in a plane are dealt. These results are + called 'tiles' - and are modeled by a TTile instance. + These images MUST BE wrapped to INTEGER POSITIONS on the reference, and are intended so that + a PIXEL corresponds to a UNIT SQUARE. + - Given one such calculation procedure, it MUST BE SIMULABLE, so that children calculations + are invoked 'faithfully' with respect to the actual calculation. + This is necessary to predict results in advance in force of which we can efficiently store + results in the cache, releasing them when they will no longer be required. + +Now, the principal classes dealing with this job are: + + - One TFxCacheManager per render instance, used to store predictive informations. + - A set of TFxCacheManagerDelegate per render instance, used to store references to cache resources + and providing reasons for caching results. + - The ResourceBuilder interface class, used by users to access the smart caching ensemble. This class + implements the actual resources build and simulation code. + +*/ + +//**************************************************************************************************** +// Preliminaries +//**************************************************************************************************** + +namespace +{ +//Global variables + +//----------------------------------------------------------------------------------------- + +//Utility functions + +inline QRect toQRect(const TRect &r) { return QRect(r.x0, r.y0, r.getLx(), r.getLy()); } +inline TRect toTRect(const QRect &r) { return TRect(r.left(), r.top(), r.right(), r.bottom()); } +inline QPoint toQPoint(const TPoint &p) { return QPoint(p.x, p.y); } + +inline bool isEmpty(const TRectD &rect) { return rect.x0 >= rect.x1 || rect.y0 >= rect.y1; } +inline void enlargeToI(TRectD &r) +{ + TRectD temp(tfloor(r.x0), tfloor(r.y0), tceil(r.x1), tceil(r.y1)); + if (!isEmpty(temp)) + r = temp; //Since r could have TConsts::infiniteRectD-like coordinates... +} + +//Qt's contains actually returns QRegion::intersected... I wonder why... +inline bool contains(const QRegion ®ion, const TRect &rect) +{ + return QRegion(toQRect(rect)).subtracted(region).isEmpty(); +} + +bool getTilesToBuild(const ResourceData &data, const TRectD &rect, + std::vector &rectsToCalculate); +} + +//**************************************************************************************************** +// TFxCacheManager Generator +//**************************************************************************************************** + +class TFxCacheManagerGenerator : public TRenderResourceManagerGenerator +{ +public: + TFxCacheManagerGenerator() : TRenderResourceManagerGenerator(true) {} + + TRenderResourceManager *operator()() { return new TFxCacheManager; } +}; + +MANAGER_FILESCOPE_DECLARATION(TFxCacheManager, TFxCacheManagerGenerator); + +//**************************************************************************************************** +// TFxCacheManager implementation +//**************************************************************************************************** + +class TFxCacheManager::Imp +{ +public: + typedef std::map ResourceInstanceDataMap; + + ResourceInstanceDataMap m_resourcesData; + std::map m_rawData; + int m_renderStatus; + + QMutex m_mutex; + +public: + void prepareTilesToCalculate(ResourceDeclaration &data); + inline void subdivideIntoSmallerTiles(const TRectD &rect, std::vector &tileSet); + void recursiveRectSubdivide( + std::vector &results, TRasterFx *fx, + const TRectD &rect, double frame, const TRenderSettings &info, + int dropTol = (std::numeric_limits::max)()); +}; + +//**************************************************************************************************** +// Methods implementation +//**************************************************************************************************** + +//======================== +// TFxCacheManager +//------------------------ + +TFxCacheManager::TFxCacheManager() + : m_imp(new Imp) +{ +} + +//----------------------------------------------------------------------------------- + +TFxCacheManager::~TFxCacheManager() +{ + //Release all the static-cached images + std::set::iterator it; + for (it = m_staticCacheIds.begin(); it != m_staticCacheIds.end(); ++it) + TImageCache::instance()->remove(*it); + + delete m_imp; +} + +//----------------------------------------------------------------------------------- + +TFxCacheManager *TFxCacheManager::instance() +{ + return static_cast( + TFxCacheManager::gen()->getManager(TRenderer::renderId())); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::add(const std::string &cacheId, TImageP img) +{ + TImageCache::instance()->add(cacheId, img); + + QMutexLocker locker(&m_imp->m_mutex); + m_staticCacheIds.insert(cacheId); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::remove(const std::string &cacheId) +{ + TImageCache::instance()->remove(cacheId); + + QMutexLocker locker(&m_imp->m_mutex); + m_staticCacheIds.erase(cacheId); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::install(TFxCacheManagerDelegate *managerDelegate) +{ + m_delegates.insert(managerDelegate); +} + +//----------------------------------------------------------------------------------- + +/*void TFxCacheManager::install(TFxCacheManagerListener* listener) +{ + m_listeners.insert(listener); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::notifyResourceUpload(const TCacheResourceP& resource, const TRect& rect) +{ + std::set::iterator it; + for(it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->onResourceUpload(resource, rect); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::notifyResourceDownload(const TCacheResourceP& resource, const TRect& rect) +{ + std::set::iterator it; + for(it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->onResourceDownload(resource, rect); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::notifyPredictedRelease(const TCacheResourceP& resource) +{ + std::set::iterator it; + for(it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->onPredictedRelease(resource); +}*/ + +//**************************************************************************************************** +// Resources dealing +//**************************************************************************************************** + +void TFxCacheManager::declareResource( + const string &alias, const TFxP &fx, + const TRectD &rect, double frame, const TRenderSettings &rs, + bool subtileable) +{ + Imp::ResourceInstanceDataMap::iterator it; + it = m_imp->m_resourcesData.insert(std::make_pair(alias, ResourceDeclaration())).first; + it->second.m_rawData = + &m_imp->m_rawData.insert(std::make_pair(&it->second, ResourceDeclaration::RawData())).first->second; + + ResourceDeclaration::RawData &rawData = *it->second.m_rawData; + + //Assign the sim data + rawData.m_fx = fx; + rawData.m_tiles.push_back(rect); + rawData.m_rs = rs; + rawData.m_frame = frame; + //rawData.m_bbox = bbox; + rawData.m_subtileable = subtileable; +} + +//----------------------------------------------------------------------------------- + +ResourceData TFxCacheManager::getResource( + const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs) +{ + TCacheResourceP result, temp; + + //Seek the associated infos + Imp::ResourceInstanceDataMap::iterator jt = + m_imp->m_resourcesData.find(alias); + ResourceDeclaration *decl = (jt == m_imp->m_resourcesData.end()) ? 0 : &jt->second; + + //Search the resource in cached mode. + //TCacheResourcePool* pool = TCacheResourcePool::instance(); + //pool->beginCachedSearch(); + + //Ask every installed delegate if it's managing - or want to manage + //the passed resource specs. + std::set::iterator it; + for (it = m_delegates.begin(); it != m_delegates.end(); ++it) { + (*it)->getResource(temp, alias, fx, frame, rs, decl); + if (!result && temp) + result = temp; + } + + //pool->endCachedSearch(); + + return ResourceData(decl, result); +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::onRenderStatusStart(int renderStatus) +{ + //Store current render status + m_imp->m_renderStatus = renderStatus; + +#ifdef WRITESTACK + if (renderStatus == TRenderer::TESTRUN) { + DIAGNOSTICS_GLOSTRSET("status", "test"); + DIAGNOSTICS_GLOSET("testInst", DIAGNOSTICS_GLOGET("compInst") + 1); + DIAGNOSTICS_GLOSTRSET("instVar", QString::number(DIAGNOSTICS_GLOGET("testInst"))); + DIAGNOSTICS_GLOSTRSET("testRenderStr", "Render #" + QString::number(DIAGNOSTICS_GLOGET("testInst")) + " | "); + } else if (renderStatus == TRenderer::COMPUTING) { + DIAGNOSTICS_GLOSTRSET("status", "comp"); + DIAGNOSTICS_GLOSTRSET("instVar", QString::number(DIAGNOSTICS_GLOGET("compInst"))); + DIAGNOSTICS_GLOSTRSET("compRenderStr", "Render #" + QString::number(DIAGNOSTICS_GLOGET("compInst")) + " | "); + DIAGNOSTICS_GLOSET(DIAGNOSTICS_THRSTRGET("stackVar"), 0); + } +#endif +} + +//----------------------------------------------------------------------------------- + +void TFxCacheManager::onRenderStatusEnd(int renderStatus) +{ + if (renderStatus == TRenderer::FIRSTRUN) { + Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData; + + Imp::ResourceInstanceDataMap::iterator it; + for (it = resMap.begin(); it != resMap.end();) { + m_imp->prepareTilesToCalculate(it->second); + + //Cannot be done. The resource could still be feasible to caching, + //due to external requests. + + //Erase all resource datas which have been declared and prepared only once + /*if(it->second.m_tiles.size() == 1 && it->second.m_simData->m_tiles.size() == 1) + { + it = resMap.erase(it); + continue; + }*/ + + ++it; + } + } else if (renderStatus == TRenderer::TESTRUN) { + Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData; + Imp::ResourceInstanceDataMap::iterator it; + for (it = resMap.begin(); it != resMap.end();) { + //Release all resource declarations which are declared to be used only once. + if (it->second.m_tiles.size() == 1 && it->second.m_tiles[0].m_refCount == 1) { + Imp::ResourceInstanceDataMap::iterator jt = it++; + resMap.erase(jt); + continue; + } + + //In any case, release all simulation datas - they are no longer useful. + //An associated cache resource avoids deletion only in case some manager + //retained it. + it->second.m_rawData = 0; + + ++it; + } + +#ifdef WRITEGENERAL + DIAGNOSTICS_SET("Declarations used more than once", resMap.size()); +#endif + + m_imp->m_rawData.clear(); + } +#ifdef WRITEGENERAL + else { + //Print the number of not depleted declarations + Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData; + Imp::ResourceInstanceDataMap::iterator it; + + DIAGNOSTICS_ADD(prefixErr + "Computing | Declarations survived after Test Run", resMap.size()); + if (resMap.size() > 0) { + for (it = resMap.begin(); it != resMap.end(); ++it) { + DIAGNOSTICS_STR(prefixErr + "Survived Declarations | " + QString::fromStdString(it->first).left(40)); + } + } + } +#endif +} + +//**************************************************************************************************** +// Tiles to calculate - methods +//**************************************************************************************************** + +void TFxCacheManager::Imp::prepareTilesToCalculate(ResourceDeclaration &data) +{ + //First, build the total sum of declared tiles + TRectD sum; + int tilesCount = data.m_rawData->m_tiles.size(); + + for (int i = 0; i < tilesCount; ++i) + sum += data.m_rawData->m_tiles[i]; + + //Intersect the sum with bbox and ensure integer geometry + //sum *= data.m_rawData->m_bbox; + enlargeToI(sum); + + if (!data.m_rawData->m_subtileable) { + data.m_tiles.push_back(sum); + return; + } + + TRasterFx *fx = dynamic_cast(data.m_rawData->m_fx.getPointer()); + + //Now, subdivide the sum + recursiveRectSubdivide(data.m_tiles, fx, sum, data.m_rawData->m_frame, data.m_rawData->m_rs); +} + +//--------------------------------------------------------------------------- + +//! Calculates at max 4 smaller subrects of passed one. Returns true or false whether the subdivision was +//! successfully applied. +inline void TFxCacheManager::Imp::subdivideIntoSmallerTiles( + const TRectD &rect, std::vector &tileSet) +{ + //Find the greater rect edge + TRectD subTile1, subTile2; + if (rect.getLx() > rect.getLy()) { + int sep = rect.x0 + tceil(0.5 * rect.getLx()); + subTile1 = TRectD(rect.x0, rect.y0, sep, rect.y1); + subTile2 = TRectD(sep, rect.y0, rect.x1, rect.y1); + } else { + int sep = rect.y0 + tceil(0.5 * rect.getLy()); + subTile1 = TRectD(rect.x0, rect.y0, rect.x1, sep); + subTile2 = TRectD(rect.x0, sep, rect.x1, rect.y1); + } + + tileSet.push_back(subTile1); + tileSet.push_back(subTile2); +} + +//--------------------------------------------------------------------------- + +void TFxCacheManager::Imp::recursiveRectSubdivide( + std::vector &results, + TRasterFx *fx, + const TRectD &rect, + double frame, + const TRenderSettings &info, + int dropTol) +{ + //Here is the subdivision strategy: + // - First, since cache tiles are newly ALLOCATED, we impose the raster + // size restriction on them directly + // - Then, we check that the memory requirement for the fx (max raster size + // that it will allocate) is little. + // - As an exception to previous point, if the memory requirement stagnates + // near the same memory size, quit + //NOTE: Level images pass here, but haven't any fx. So they are currently not subdivided. + + //Retrieve the memory requirement for this input. + int memReq = fx ? fx->getMemoryRequirement(rect, frame, info) : 0; + + //In case memReq < 0, we assume a strong subdivision denial, just as if the usage was + //explicitly set as unsubdividable. + if (memReq < 0) { + results.push_back(rect); + return; + } + + if ((memReq > info.m_maxTileSize && memReq < dropTol) || + TRasterFx::memorySize(rect, info.m_bpp) > info.m_maxTileSize) { + std::vector subTileRects; + subdivideIntoSmallerTiles(rect, subTileRects); + + while (!subTileRects.empty()) { + TRectD subTileRect(subTileRects.back()); + subTileRects.pop_back(); + + //Pass subdivision below, with updated drop-tolerance + recursiveRectSubdivide(results, fx, subTileRect, frame, info, + memReq - (memReq >> 2)); + + //The newly required memory must be under 3/4 of the previous. + //This is required in order to make it worth subdividing. + } + + return; + } + + results.push_back(ResourceDeclaration::TileData(rect)); +} + +//**************************************************************************************************** +// ResourceBuilder +//**************************************************************************************************** + +ResourceBuilder::ResourceBuilder( + const std::string &resourceName, + const TFxP &fx, double frame, const TRenderSettings &rs) + : m_cacheManager(TFxCacheManager::instance()), m_data(m_cacheManager->getResource(resourceName, fx, frame, rs)) +{ +#ifdef WRITESTACK + DIAGNOSTICS_THRSET("frame", frame); + DIAGNOSTICS_THRSTRSET("frameStr", "Frame " + QString::number(frame).rightJustified(4, ' ') + " | "); + DIAGNOSTICS_THRSTRSET("stackVar", + DIAGNOSTICS_GLOSTRGET("status") + "sv" + DIAGNOSTICS_GLOSTRGET("instVar") + "fr" + QString::number(frame)); + DIAGNOSTICS_THRSTRSET("ResourceName", QString::fromStdString(resourceName).left(35)); +#endif +} + +//----------------------------------------------------------------------------------- + +void ResourceBuilder::declareResource( + const string &alias, const TFxP &fx, + const TRectD &rect, double frame, const TRenderSettings &rs, + bool subtileable) +{ + TFxCacheManager::instance()->declareResource( + alias, fx, rect, frame, rs, subtileable); +} + +//----------------------------------------------------------------------------------- + +namespace +{ +// Retrieves all interesting tiles with respect to the build procedure. +// Explicitly, this refers to tiles intersecting the required rect, that are either +// not in the resource, or supposed to be not. +// Returns true if the required tile is contained in the sum of the predicted ones +// (which should definitely happen if the predictive step is coherent with the +// computation one). +bool getTilesToBuild( + const ResourceData &data, const TRectD &rect, std::vector &rectsToCalculate) +{ + assert(data.first); //The declaration must DEFINITELY be present + assert(data.second); //The resource should be present here + + //Now, fill in with all prepared rects which intersect input rect and are not + //already in the resource + + std::vector &preparedRects = + data.first->m_tiles; + std::vector::iterator jt; + + TRectD sum; + for (jt = preparedRects.begin(); jt != preparedRects.end(); ++jt) { + sum += jt->m_rect; + + if (!(isEmpty(rect * jt->m_rect) || jt->m_calculated)) + rectsToCalculate.push_back(&(*jt)); + } + + return sum.contains(rect); +} +} // namespace + +//----------------------------------------------------------------------------------- + +void ResourceBuilder::simBuild(const TRectD &rect) +{ + //Retrieve the render status + int renderStatus = m_cacheManager->m_imp->m_renderStatus; + + //In the initial precomputing stage, just retrieve all the declarations without efforts. + if (renderStatus == TRenderer::FIRSTRUN) { + simCompute(rect); + return; + } + + //Perform the test run + if (renderStatus == TRenderer::TESTRUN) { + if (!(m_data.first && m_data.second)) + return; + +#ifdef WRITESTACK + QString resName(DIAGNOSTICS_THRSTRGET("ResourceName")); + + QString renderStr(DIAGNOSTICS_GLOSTRGET("testRenderStr")); + QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr")); + DIAGNOSTICS_NUMBEREDSTRSET(prefixTest + renderStr + frameStr, + DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName, + ::traduce(rect), 4); + DIAGNOSTICS_PUSHAUTO("parentResource", + QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla); +#endif + + //Retrieve the tiles to build + std::vector &tiles = m_data.first->m_tiles; + + //For every tile intersecting rect + std::vector::iterator it; + for (it = tiles.begin(); it != tiles.end(); ++it) { + ResourceDeclaration::TileData &tileData = *it; + + if (!isEmpty(tileData.m_rect * rect)) { + //If the tile ref count == 0, assume that this tile has not yet been simComputed. + //Do so, then; further, add 1 to the number of predicted actively accessed tiles. + if (tileData.m_refCount == 0) { +#ifdef WRITESUBRECTS + DIAGNOSTICS_NUMBEREDSTRSET(prefixTest + renderStr + frameStr, + DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName, + ::traduce(tileData.m_rect), 4); + DIAGNOSTICS_PUSHAUTO("parentResource", + QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla2); +#endif + + simCompute(tileData.m_rect); + ++m_data.first->m_tilesCount; + } + + //Add a reference to this tile + ++tileData.m_refCount; + + if (m_data.second) { + QMutexLocker locker(m_data.second->getMutex()); + + TRect tileRectI( + tileData.m_rect.x0, tileData.m_rect.y0, + tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1); + + m_data.second->addRef2(tileRectI); + } + } + } + + return; + } + + //In this case, the simulation is used to declare that this rect will NOT be calculated. + //So, this is the behaviour: 1 refCount is depleted by all cells intersecting the rect + // - if the refCount is still >0, then another request will occur that will make the + //tile calculated. If it is == 0, and the tile has not yet been calculated, then the tile + //will supposedly be NO MORE calculated, so the simCompute has to be launched on that tile, too. + + if (renderStatus == TRenderer::COMPUTING) { + if (!(m_data.first && m_data.second)) + return; + + QMutexLocker locker(m_data.second->getMutex()); + + //Retrieve the tiles to build + std::vector &tiles = m_data.first->m_tiles; + + //Please note that the request should definitely be fitting the predicted results, + //since the original rect which generated these simCompute calls must have been fitting. + + //For every tile to build + std::vector::iterator it; + for (it = tiles.begin(); it != tiles.end(); ++it) { + ResourceDeclaration::TileData &tileData = *it; + + if (!isEmpty(tileData.m_rect * rect)) { + if (tileData.m_refCount <= 0) + continue; + + if (--tileData.m_refCount == 0 && !tileData.m_calculated) { + --m_data.first->m_tilesCount; + simCompute(tileData.m_rect); + } + + if (m_data.second) { + TRect tileRectI( + tileData.m_rect.x0, tileData.m_rect.y0, + tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1); + + m_data.second->release2(tileRectI); + } + } + } + } +} + +//----------------------------------------------------------------------------------- + +void ResourceBuilder::build(const TRectD &tileRect) +{ +#ifdef WRITESTACK + QString resName(DIAGNOSTICS_THRSTRGET("ResourceName")); + + QString renderStr(DIAGNOSTICS_GLOSTRGET("compRenderStr")); + QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr")); + DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr, + DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName, + ::traduce(tileRect), 4); + DIAGNOSTICS_PUSHAUTO("parentResource", + QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla); +#endif + + //If there is no resource, just compute the tile directly. + if (!m_data.second) { +#ifdef WRITEGENERAL + if (m_data.first) + if (m_data.first->m_tilesCount > 0) + DIAGNOSTICS_ADD(prefixErr + "Computing | No-resource build, active decl, tilesCount > 0", 1); + else + DIAGNOSTICS_ADD(prefixErr + "Computing | No-resource build, active decl, tilesCount <= 0", 1); +#endif + + //assert(!m_data.first); //Should have been erased before the COMPUTING run. + compute(tileRect); + return; + } + + //Since this function must be thread-safe, use the appropriate synchronization tool. + QMutexLocker locker(m_data.second->getMutex()); + + //Without declaration, you can just deal with the required tile. + if (!(m_data.first && m_data.first->m_tilesCount > 0)) { +#ifdef WRITEGENERAL + if (!m_data.first) + DIAGNOSTICS_ADD("#error.txt | Resources without declaration", 1); + else + DIAGNOSTICS_ADD("#error.txt | Resources with declaration, tilesCount <=0", 1); +#endif + + if (download(m_data.second)) + return; + + compute(tileRect); + + //Since there is an associated resource, the calculated content is supposedly + //an interesting one. Upload it. + upload(m_data.second); + + return; + } + + //Now, both the declaration and the resource exist. + + //Retrieve the predicted tile that must be built in place of tile. + TDimension dim(tileRect.getLx(), tileRect.getLy()); + + std::vector tiles; + bool fittingPrediction = getTilesToBuild(m_data, tileRect, tiles); + + if (!fittingPrediction) { +#ifdef WRITEGENERAL + DIAGNOSTICS_ADD(prefixErr + "Computing | Not fitting tiles", 1); +#endif + + //If the required tile is not fitting the prediction, we assume it is a + //full un-predicted one - so no reference count will be updated (this would comply + //with the simCompute() method, in case we assume that mis-predicted + //computes are always prudently built IN EXCESS). + + //For now, just calculate it and stop. + locker.unlock(); + + compute(tileRect); + return; + } + + //If necessary, calculate something + if (tiles.size() > 0) { + //For every tile to build + std::vector::iterator it; + for (it = tiles.begin(); it != tiles.end(); ++it) { + ResourceDeclaration::TileData &tileData = **it; + + //If the tile can be downloaded from the resource, it's because it has actually + //been calculated by another render process - either a concurrent one, or any + //which has written this resource part on disk storage. + + //Since reference counts built in the simCompute assume that tiles downloaded + //from the resource have been calculated in THIS very render process, + //therefore having tileData.m_calculated == true, in this case + //heir refCounts must be updated since no computing will happen on them + //due to the predicted node builds of this resource. + TRect tileRectI( + tileData.m_rect.x0, tileData.m_rect.y0, + tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1); + + if (m_data.second->canDownloadAll(tileRectI)) { + if (!tileData.m_calculated && tileData.m_refCount > 0) { + /*#ifdef WRITESTACK + QString renderStr(DIAGNOSTICS_GLOSTRGET("compRenderStr")); + QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr")); + DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr, + DIAGNOSTICS_THRSTRGET("stackVar"), "$ > " + resName, + ::traduce(tileData.m_rect), 4); +#endif*/ + + //Deplete children refsCount - rely on the simulated procedure + simCompute(tileData.m_rect); + } + } else { +#ifdef WRITESUBRECTS + DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr, + DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName, + ::traduce(tileData.m_rect), 4); + DIAGNOSTICS_PUSHAUTO("parentResource", + QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla2); +#endif + + //Compute the tile to be calculated + compute(tileData.m_rect); + if (tileData.m_refCount > 0) + tileData.m_calculated = true; + + //Upload the tile into the resource - do so even if the tile + //was unpredicted. In this case, we rely on the resource refCount to + //provide the deallocations... Should be so? + upload(m_data.second); + } + } + } + + //Finally, download the built resource in the required tile + bool ret = download(m_data.second); + assert(ret); + +#ifdef WRITESTACK + if (!ret) + DIAGNOSTICS_STRSET(prefixErr + "Download falliti | " + + DIAGNOSTICS_GLOSTRGET("compRenderStr") + DIAGNOSTICS_THRSTRGET("frameStr") + + QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), + "CROP #" + QString::number(DIAGNOSTICS_GLOGET("crStack"))); +#endif + + //Deplete a usage for all tiles intersecting the downloaded one. Fully depleted + //tiles become unpredicted from now on. + std::vector &resTiles = m_data.first->m_tiles; + std::vector::iterator it; + for (it = resTiles.begin(); it != resTiles.end(); ++it) { + ResourceDeclaration::TileData &tileData = *it; + + if (!isEmpty(tileData.m_rect * tileRect)) { + if (tileData.m_refCount <= 0) { +#ifdef WRITEGENERAL + DIAGNOSTICS_ADD(prefixErr + "Computing | Over-used subtiles", 1); +#endif + + continue; + } + + if (--tileData.m_refCount == 0) { + tileData.m_calculated = false; + --m_data.first->m_tilesCount; + } + + TRect tileRectI( + tileData.m_rect.x0, tileData.m_rect.y0, + tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1); + + m_data.second->release2(tileRectI); + } + } + + //If the declaration has been completely used up, destroy it. + //NOTE: Keeping the declarations is useful for diagnostic purposes. The following code + //could be reactivated - but declarations tend to be lightweight now... + //NOTE: If re-enabled, competing mutexes must be set where the resourcesData map is used... + /*if(m_data.first->m_tilesCount <= 0) + { + QMutexLocker locker(&m_cacheManager->m_imp->m_mutex); + m_cacheManager->m_imp->m_resourcesData.erase(m_data.second->getName()); + }*/ +} diff --git a/toonz/sources/common/tfx/tmacrofx.cpp b/toonz/sources/common/tfx/tmacrofx.cpp new file mode 100644 index 0000000..018d294 --- /dev/null +++ b/toonz/sources/common/tfx/tmacrofx.cpp @@ -0,0 +1,598 @@ + + +#include "tmacrofx.h" + +// TnzBase includes +#include "tparamcontainer.h" +#include "tfxattributes.h" + +// TnzCore includes +#include "tstream.h" + +//-------------------------------------------------- + +namespace +{ + +class MatchesFx +{ +public: + MatchesFx(const TFxP &fx) : m_fx(fx) {} + + bool operator()(const TFxP &fx) + { + return m_fx.getPointer() == fx.getPointer(); + } + + TFxP m_fx; +}; + +//-------------------------------------------------- + +void pushParents(const TFxP &root, std::vector &fxs, const std::vector &selectedFxs) +{ + int i, count = root->getInputPortCount(); + if (count == 0) { + std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root)); + if (found == fxs.end()) + fxs.push_back(root); + return; + } + for (i = 0; i < count; i++) { + TFxP inutFx = root->getInputPort(i)->getFx(); + std::vector::const_iterator found = std::find_if(selectedFxs.begin(), selectedFxs.end(), MatchesFx(inutFx)); + if (found != selectedFxs.end()) + pushParents(inutFx, fxs, selectedFxs); + } + std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root)); + if (found == fxs.end()) + fxs.push_back(root); +} + +//-------------------------------------------------- + +std::vector sortFxs(const std::vector &fxs) +{ + std::vector app; + std::vector roots; + //find fxs that could be in back of the vector. + int i; + for (i = 0; i < (int)fxs.size(); i++) { + TFxP fx = fxs[i]; + int j, count = (int)fx->getOutputConnectionCount(); + if (count == 0) { + roots.push_back(fx); + continue; + } + for (j = 0; j < count; j++) { + TFxP connectedFx = fx->getOutputConnection(j)->getOwnerFx(); + std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(connectedFx)); + if (found == fxs.end()) { + roots.push_back(fx); + break; + } + } + } + for (i = 0; i < (int)roots.size(); i++) + pushParents(roots[i], app, fxs); + assert(fxs.size() == app.size()); + return app; +} + +//-------------------------------------------------- + +// raccoglie tutti i parametri dai vari TFx e li assegna anche alla macro +void collectParams(TMacroFx *macroFx) +{ + int k; + for (k = 0; k < (int)macroFx->m_fxs.size(); k++) { + TFxP fx = macroFx->m_fxs[k]; + int j; + for (j = 0; j < fx->getParams()->getParamCount(); j++) + macroFx->getParams()->add(fx->getParams()->getParamVar(j)->clone()); + } +} + +} // anonymous namespace + +//-------------------------------------------------- + +bool TMacroFx::analyze(const vector &fxs, + TFxP &root, + vector &roots, + vector &leafs) +{ + if (fxs.size() == 1) + return false; + else { + leafs.clear(); + roots.clear(); + std::vector::const_iterator it = fxs.begin(); + for (; it != fxs.end(); ++it) { + TFxP fx = *it; + int inputInternalConnection = 0; + int inputExternalConnection = 0; + int outputInternalConnection = 0; + int outputExternalConnection = 0; + + int i; + + // calcola se ci sono connessioni in input dall'esterno + // verso l'interno e/o internamente a orderedFxs + int inputPortCount = fx->getInputPortCount(); + for (i = 0; i < inputPortCount; ++i) { + TFxPort *inputPort = fx->getInputPort(i); + TFx *inputPortFx = inputPort->getFx(); + if (inputPortFx) { + if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(inputPortFx)) != fxs.end()) + ++inputInternalConnection; + else + ++inputExternalConnection; + } + } + + // calcola se ci sono connessioni in output dall'interno + // verso l'esterno e/o internamente a orderedFxs + int outputPortCount = fx->getOutputConnectionCount(); + for (i = 0; i < outputPortCount; ++i) { + TFxPort *outputPort = fx->getOutputConnection(i); + TFx *outputFx = outputPort->getOwnerFx(); + if (outputFx) { + if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(outputFx)) != fxs.end()) + ++outputInternalConnection; + else + ++outputExternalConnection; + } + } + + // se fx e' una radice + if ((outputExternalConnection > 0) || + (outputExternalConnection == 0 && outputInternalConnection == 0)) { + root = fx; + roots.push_back(fx); + } + + // se fx e' una foglia + if (inputExternalConnection > 0 || fx->getInputPortCount() == 0 || + (inputExternalConnection == 0 && inputInternalConnection < fx->getInputPortCount())) { + leafs.push_back(fx); + } + } + + if (roots.size() != 1) + return false; + else { + if (leafs.size() == 0) + return false; + } + + return true; + } +} + +//-------------------------------------------------- + +bool TMacroFx::analyze(const vector &fxs) +{ + TFxP root = 0; + std::vector leafs; + std::vector roots; + return analyze(fxs, root, roots, leafs); +} + +//-------------------------------------------------- + +bool TMacroFx::isaLeaf(TFx *fx) const +{ + int count = fx->getInputPortCount(); + if (count == 0) + return true; + + for (int i = 0; i < count; ++i) { + TFxPort *port = fx->getInputPort(i); + TFx *inputFx = port->getFx(); + if (inputFx) { + if (std::find_if(m_fxs.begin(), m_fxs.end(), MatchesFx(inputFx)) == m_fxs.end()) { + // il nodo di input non appartiene al macroFx + return true; + } + } else { + // la porta di input non e' connessa + return true; + } + } + + // tutte le porte di input sono connesse verso nodi appartenenti al macroFx + return false; +} + +//-------------------------------------------------- + +TMacroFx::TMacroFx() : m_isEditing(false) {} + +//-------------------------------------------------- + +TMacroFx::~TMacroFx() {} + +//-------------------------------------------------- + +TFx *TMacroFx::clone(bool recursive) const +{ + int n = m_fxs.size(); + vector clones(n); + std::map table; + std::map::iterator it; + int i, rootIndex = -1; + // nodi + for (i = 0; i < n; ++i) { + TFx *fx = m_fxs[i].getPointer(); + assert(fx); + clones[i] = fx->clone(false); + assert(table.count(fx) == 0); + table[fx] = i; + if (fx == m_root.getPointer()) + rootIndex = i; + TFx *linkedFx = fx->getLinkedFx(); + if (linkedFx && table.find(linkedFx) != table.end()) + clones[i]->linkParams(clones[table[linkedFx]].getPointer()); + } + assert(rootIndex >= 0); + // connessioni + for (i = 0; i < n; i++) { + TFx *fx = m_fxs[i].getPointer(); + for (int j = 0; j < fx->getInputPortCount(); j++) { + TFxPort *port = fx->getInputPort(j); + TFx *inputFx = port->getFx(); + if (!inputFx) + continue; + it = table.find(inputFx); + if (it == table.end()) { + // il j-esimo input di fx e' esterno alla macro + if (recursive) + clones[i]->connect(fx->getInputPortName(j), inputFx->clone(true)); + } else { + // il j-esimo input di fx e' interno alla macro + clones[i]->connect(fx->getInputPortName(j), clones[it->second].getPointer()); + } + } + } + + //TFx *rootClone = + // const_cast(this)-> + // clone(m_root.getPointer(), recursive, visited, clones); + + TMacroFx *clone = TMacroFx::create(clones); + clone->setName(getName()); + clone->setFxId(getFxId()); + + //Copy the index of the passive cache manager. + clone->getAttributes()->passiveCacheDataIdx() = getAttributes()->passiveCacheDataIdx(); + + assert(clone->getRoot() == clones[rootIndex].getPointer()); + + return clone; +} + +//-------------------------------------------------- + +bool TMacroFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) +{ + return m_root->doGetBBox(frame, bBox, info); +} + +//-------------------------------------------------- + +void TMacroFx::doDryCompute(TRectD &rect, + double frame, + const TRenderSettings &info) +{ + assert(m_root); + m_root->dryCompute(rect, frame, info); +} + +//-------------------------------------------------- + +void TMacroFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) +{ + assert(m_root); + m_root->compute(tile, frame, ri); +} + +//-------------------------------------------------- + +TFxTimeRegion TMacroFx::getTimeRegion() const +{ + return m_root->getTimeRegion(); +} + +//-------------------------------------------------- + +string TMacroFx::getPluginId() const +{ + return "Base"; +} + +//-------------------------------------------------- + +void TMacroFx::setRoot(TFx *root) +{ + m_root = root; + // TFx::m_imp->m_outputPort = root->m_imp->m_outputPort; +} + +//-------------------------------------------------- + +TFx *TMacroFx::getRoot() const +{ + return m_root.getPointer(); +} + +//-------------------------------------------------- + +TFx *TMacroFx::getFxById(const wstring &id) const +{ + int i; + for (i = 0; i < (int)m_fxs.size(); i++) { + TFx *fx = m_fxs[i].getPointer(); + if (fx->getFxId() == id) + return fx; + } + return 0; +} + +//-------------------------------------------------- + +const vector &TMacroFx::getFxs() const +{ + return m_fxs; +} + +//-------------------------------------------------- + +string TMacroFx::getMacroFxType() const +{ + string name = getFxType() + "("; + for (int i = 0; i < (int)m_fxs.size(); i++) { + if (i > 0) + name += ","; + if (TMacroFx *childMacro = dynamic_cast(m_fxs[i].getPointer())) + name += childMacro->getMacroFxType(); + else + name += m_fxs[i]->getFxType(); + } + return name + ")"; +} + +//-------------------------------------------------- + +TMacroFx *TMacroFx::create(const vector &fxs) +{ + std::vector leafs; + std::vector roots; + TFxP root = 0; + + vector orederedFxs = sortFxs(fxs); + + // verifica che gli effetti selezionati siano idonei ad essere raccolti + // in una macro. Ci deve essere un solo nodo terminale + // (roots.size()==1, roots[0] == root) e uno o piu' nodi di ingresso + // (assert leafs.size()>0) + if (!analyze(orederedFxs, root, roots, leafs)) + return 0; + + // ----------------------------- + + TMacroFx *macroFx = new TMacroFx; + + // tutti i nodi vengono spostati (e non copiati) nella macro stessa + std::vector::const_iterator it = orederedFxs.begin(); + for (; it != orederedFxs.end(); ++it) + macroFx->m_fxs.push_back(*it); + + // i nodi di ingresso vengono messi in collegamento con le + // porte di ingresso della macro + for (int i = 0; i < (int)leafs.size(); i++) { + TFxP fx = leafs[i]; + int k = 0; + int count = fx->getInputPortCount(); + for (; k < count; k++) { + TFxPort *port = fx->getInputPort(k); + string portName = fx->getInputPortName(k); + string fxId = toString(fx->getFxId()); + portName += "_" + toString(macroFx->getInputPortCount()) + "_" + fxId; + TFx *portFx = port->getFx(); + if (portFx) { + // se la porta k-esima del nodo di ingresso i-esimo e' collegata + // ad un effetto, la porta viene inserita solo se l'effetto non fa + // gia' parte della macro + if (std::find_if(orederedFxs.begin(), orederedFxs.end(), MatchesFx(portFx)) == orederedFxs.end()) + macroFx->addInputPort(portName, *port); + } else + macroFx->addInputPort(portName, *port); + } + } + + // le porte di uscita di root diventano le porte di uscita della macro + int count = root->getOutputConnectionCount(); + int k = count - 1; + for (; k >= 0; --k) { + TFxPort *port = root->getOutputConnection(k); + port->setFx(macroFx); + } + + macroFx->setRoot(root.getPointer()); + + // tutti i parametri delle funzioni figlie diventano parametri della macro + collectParams(macroFx); + return macroFx; +} + +//-------------------------------------------------- + +bool TMacroFx::canHandle(const TRenderSettings &info, double frame) +{ + return m_root->canHandle(info, frame); +} + +//-------------------------------------------------- + +string TMacroFx::getAlias(double frame, const TRenderSettings &info) const +{ + string alias = getFxType(); + alias += "["; + + // alias degli effetti connessi alle porte di input separati da virgole + // una porta non connessa da luogo a un alias vuoto (stringa vuota) + int i; + for (i = 0; i < getInputPortCount(); i++) { + TFxPort *port = getInputPort(i); + if (port->isConnected()) { + TRasterFxP ifx = port->getFx(); + assert(ifx); + alias += ifx->getAlias(frame, info); + } + alias += ","; + } + + // alias dei valori dei parametri dell'effetto al frame dato + for (int j = 0; j < (int)m_fxs.size(); j++) { + alias += (j == 0) ? "(" : ",("; + for (i = 0; i < m_fxs[j]->getParams()->getParamCount(); i++) { + if (i > 0) + alias += ","; + TParam *param = m_fxs[j]->getParams()->getParam(i); + alias += param->getName() + "=" + param->getValueAlias(frame, 2); + } + alias += ")"; + } + + alias += "]"; + return alias; +} + +//-------------------------------------------------- + +void TMacroFx::compatibilityTranslatePort(int major, int minor, std::string &portName) +{ + // Reroute translation to the actual fx associated to the port + const std::string &fxId = portName.substr(portName.find_last_of('_') + 1, + std::string::npos); + + if (TFx *fx = getFxById(toWideString(fxId))) { + size_t opnEnd = portName.find_first_of('_'); + + std::string originalPortName = portName.substr(0, opnEnd); + fx->compatibilityTranslatePort(major, minor, originalPortName); + + portName.replace(0, opnEnd, originalPortName); + } + + // Seems that at a certain point, the port name got extended... + if (VersionNumber(major, minor) == VersionNumber(1, 16)) { + for (int i = 0; i < getInputPortCount(); ++i) { + const std::string &name = getInputPortName(i); + if (name.find(portName) != string::npos) { + portName = name; + break; + } + } + } +} + +//-------------------------------------------------- + +void TMacroFx::loadData(TIStream &is) +{ + VersionNumber tnzVersion = is.getVersion(); + string tagName; + while (is.openChild(tagName)) { + if (tagName == "root") { + TPersist *p = 0; + is >> p; + m_root = dynamic_cast(p); + } else if (tagName == "nodes") { + while (!is.eos()) { + TPersist *p = 0; + is >> p; + + // NOTE: In current implementation p is sharedly owned by is - it's automatically + // released upon stream destruction if the below assignment fails + + if (TFx *fx = dynamic_cast(p)) { + m_fxs.push_back(fx); + } + } + } else if (tagName == "ports") { + int i = 0; + while (is.matchTag(tagName)) { + if (tagName == "port") { + string name = is.getTagAttribute("name"); + if (tnzVersion < VersionNumber(1, 16) && name != "") { + TRasterFxPort *port = new TRasterFxPort(); + addInputPort(name, *port); + } else { + name = is.getTagAttribute("name_inFx"); + if (tnzVersion < VersionNumber(1, 17) && tnzVersion != VersionNumber(0, 0)) + name.insert(name.find("_"), "_" + toString(i)); + + compatibilityTranslatePort(tnzVersion.first, tnzVersion.second, name); + + string inPortName = name; + inPortName.erase(inPortName.find("_"), inPortName.size() - 1); + + string inFxId = name; + inFxId.erase(0, inFxId.find_last_of("_") + 1); + + for (int i = 0; i < (int)m_fxs.size(); i++) { + wstring fxId = m_fxs[i]->getFxId(); + if (fxId == toWideString(inFxId)) { + if (TFxPort *port = m_fxs[i]->getInputPort(inPortName)) + addInputPort(name, *port); + } + } + } + i++; + } else + throw TException("unexpected tag " + tagName); + } + } else if (tagName == "super") { + TRasterFx::loadData(is); + } else + throw TException("unexpected tag " + tagName); + is.closeChild(); + } + collectParams(this); +} + +//-------------------------------------------------- + +void TMacroFx::saveData(TOStream &os) +{ + int i; + os.openChild("root"); + TPersist *p = m_root.getPointer(); + os << p; + os.closeChild(); + os.openChild("nodes"); + for (i = 0; i < (int)m_fxs.size(); i++) { + TFxP fx = m_fxs[i]; + TPersist *p = fx.getPointer(); + os << p; + } + os.closeChild(); + os.openChild("ports"); + for (i = 0; i < getInputPortCount(); i++) { + std::string portName = getInputPortName(i); + std::map attr; + attr["name_inFx"] = portName; + os.openCloseChild("port", attr); + } + os.closeChild(); + os.openChild("super"); + TRasterFx::saveData(os); + os.closeChild(); +} + +//-------------------------------------------------- +FX_IDENTIFIER(TMacroFx, "macroFx") +//FX_IDENTIFIER_IS_HIDDEN(TMacroFx, "macroFx") diff --git a/toonz/sources/common/tfx/tpassivecachemanager.cpp b/toonz/sources/common/tfx/tpassivecachemanager.cpp new file mode 100644 index 0000000..da0a349 --- /dev/null +++ b/toonz/sources/common/tfx/tpassivecachemanager.cpp @@ -0,0 +1,1063 @@ + + +#include "tfxattributes.h" + +#include "trenderer.h" +#include "tcacheresource.h" +#include "tcacheresourcepool.h" + +#include "tpassivecachemanager.h" + +//#define USE_SQLITE_HDPOOL + +/* PRACTICAL EXPLANATION: + +The TPassiveCacheManager's purpose is that of storing render results from a specified set of fx nodes. + +When an fx is passed to the manager to be cached, a structure of type FxData is generated for the fx. +It is stored inside the manager and ASSOCIATED to the fx through an INDEX key - this one being stored +inside the fx data. + +In particular, the FxData instances contain a PERSISTANT identifier (passiveCacheId) that is saved inside +the scene data, a flag specifying the cache status of the fx node (like, if it's enabled or not), and +a description of the schematic dag below the associated fx. + +Now, when a resource request is made to the TPassiveCacheManager, it checks the FxData about the generating +fx - if it contains the flag specifying that it must be cached, a resource is allocated (if not already +present), a reference to it is stored, and finally the resource is returned. + +References to interesting resources are stored in a TABLE container, indexed by "rendering context" and +fx. A rendering context is hereby intended as a SEQUENCE of render processes where the next render of the +sequence is assumed to REPLACE the previous one. +We therefore have one context for the swatch viewer, one for File>Preview, one for the Preview Fx of each +fx node, and one context for EACH FRAME of the sceneviewer preview (one frame is not supposed to replace +another in that case). + +The table is in practice a map of maps (a kind of 'comb' structure) - where the primary map is associated +by render context and the secondary ones by fx. This mean that we are able to address and iterate contexts +easier than fxs. Values of the table are set of resources. + +RESOURCES MAINTENANCE POLICY: + +As resources use a concrete amount of memory for storage, they should be kept in the resources table only +for the time they are supposedly useful to the user. There are 2 ways of dealing with this issue. + +1) First, we can track scene changes and eliminate those resources that will no longer be accessed due to the +applied change. This happens, for example, when a level is being drawn, fxs are inserted, moved or removed. + +Level changes are delivered immediately to the ::invalidateLevel(..) method, which removes all resources +whose name contains the level's name. Unfortunately, this cannot be done at frame precision (ie you cannot +invalidate a single frame of the level, but ALL the level). + +Xsheet (schematic) changes are tracked through the schematic node description stored inside the FxData structure. +Once one such change happens, all resources update their description - if that changes, the associated resources +are released. + +The same schematic description is used to track and release resources affected by changes on a downstream fx's +parameters. + +There are also scene changes that are inherently hard to track or intercept. For example, pegbar affines changes, +fx nodes expressions referencing data not directly connected to the fx, and possibly others. These are currently +NOT handled by direct tracking. + +2) As there may be resources which escape the resource control by point (1), we need some extra control +policies. Here they are: + +We assume that the pool of resources accessed by render processes of a same render context should be CONSTANT +IF NO SCENE CHANGE HAPPENS. In other words, we are guessing that only those resources used in the last +rendering will be used in the next. Resources accessed in a render but NOT in the next (of the same context) +are released. + +However, it could be that the sequence of render processes from a context is halted from a certain moment on. +In that case, it is a waste to keep the resources accessed by its last render if no new render will ever take +place. We then assume further that a rendering context can be ENABLED or DISABLED - when a render context is +enabled, it will most likely have a next render - and therefore can keep its resources in memory. +Once a context is DISABLED, it moves the resources to a TEMPORARY context. + +Resources in the temporary context are those 'on their way for release'. They will be KEPT only if the +next rendering - INDEPENDENTLY FROM THE RENDER CONTEXT - requires them (in this case, they will be adopted +by the new context). This is necessary since context disables typically happen when a preview window closes. +It is not unfrequent that users close the preview window, modify the scene, and then restore the preview. + +CONSIDERATIONS: + +* The File>Render generates NO CONTEXT - and therefore does NOT PASSIVELY CACHE RESOURCES, since it cannot be + stated whether it is in the 'enabled' or 'disabled' state. It could be considered coherent with what tcomposer + does, by the way... + +* Our resources maintenance policy ensures that the memory usage should be stable over time - that is, no + useless resource is kept in memory. + Of course, it is possibly more restrictive than what the user may desire. For example, closing 2 preview + windows marks their resources for deletion, but only one of the two restored previews will keep its + resources intact... + +*/ + +//***************************************************************************************** +// Debug stuff +//***************************************************************************************** + +/* +#define DIAGNOSTICS +#include "diagnostics.h" + +namespace +{ + QString prefix("#Passive.txt | STACK | "); + + //-------------------------------------------------------------------------------------------------- + + inline std::string traduce(const TRectD& rect) + { + return "[" + toString(rect.x0) + " " + toString(rect.y0) + " " + + toString(rect.x1) + " " + toString(rect.y1) + "]"; + } + + //-------------------------------------------------------------------------------------------------- + + inline std::string traduce(const TAffine& aff) + { + return "(" + + toString(aff.a11) + " " + toString(aff.a12) + " " + + toString(aff.a13) + " " + toString(aff.a21) + " " + + toString(aff.a22) + " " + toString(aff.a23) + ")"; + } +} +*/ + +//***************************************************************************************** +// Preliminaries +//***************************************************************************************** + +// Local stuff - inlines +namespace +{ +inline QRect toQRect(const TRect &r) { return QRect(r.x0, r.y0, r.getLx(), r.getLy()); } +inline TRect toTRect(const QRect &r) { return TRect(r.left(), r.top(), r.right(), r.bottom()); } +} + +//--------------------------------------------------------------------------- + +TFx *TPassiveCacheManager::getNotAllowingAncestor(TFx *fx) +{ + //Trace all output ports + int outputPortsCount = fx->getOutputConnectionCount(); + /*if(!outputPortsCount) //We have no access to TApp here!! + { + //It could be a terminal fx. In that case, pass to the xsheet fx + FxDag* dag = TApp::instance()->getCurrentXsheet()->getXsheet()->getFxDag(); + if(dag->getTerminalFxs()->containsFx(fx)) + return getNotAllowingAncestor(dag->getXsheetFx()); + }*/ + + //Now, for common ports + for (int i = 0; i < outputPortsCount; ++i) { + //Find the output Fx and the port connected to our fx + TFxPort *port = fx->getOutputConnection(i); + TRasterFx *outFx = static_cast(port->getOwnerFx()); + + int portIdx, portsCount = outFx->getInputPortCount(); + for (portIdx = 0; portIdx < portsCount; ++portIdx) + if (outFx->getInputPort(portIdx) == port) + break; + assert(portIdx < portsCount); + + if (!outFx->allowUserCacheOnPort(portIdx)) + return outFx; + + TFx *naAncestor = getNotAllowingAncestor(outFx); + if (naAncestor) + return naAncestor; + } + + return 0; +} + +//***************************************************************************************** +// Resources Container Definition +//***************************************************************************************** + +template +class Table +{ +public: + typedef typename std::map Row; + +private: + std::map m_table; + + friend class Iterator; + friend class ColIterator; + +public: + typedef typename std::map::iterator RowsIterator; + + class Iterator + { + protected: + Table *m_table; + RowsIterator m_rowIt; + typename Row::iterator m_it; + + friend class Table; + Iterator(Table *table) : m_table(table) {} + + virtual void makeConsistent() + { + if (m_it == m_rowIt->second.end()) { + if (++m_rowIt == m_table->m_table.end()) + return; + m_it = m_rowIt->second.begin(); + } + } + + public: + const RowKey &row() { return m_rowIt->first; } + const ColKey &col() { return m_it->first; } + + virtual void operator++() + { + ++m_it; + makeConsistent(); + } + + virtual operator bool() { return m_rowIt != m_table->m_table.end(); } + + Val &operator*() { return m_it->second; } + Val *operator->() { return &m_it->second; } + + bool operator==(const Iterator &it) + { + return m_it == it.m_it; + } + + bool operator!=(const Iterator &it) + { + return !operator==(it); + } + }; + + class ColIterator : public Iterator + { + ColKey m_colKey; + + friend class Table; + ColIterator(Table *table, const ColKey &c) : Iterator(table), m_colKey(c) {} + + void makeConsistent() + { + Iterator::m_rowIt = Iterator::m_rowIt; + while (Iterator::m_rowIt != Iterator::m_table->m_table.end()) { + Iterator::m_it = Iterator::m_rowIt->second.find(m_colKey); + if (Iterator::m_it != Iterator::m_rowIt->second.end()) + break; + ++Iterator::m_rowIt; + } + } + + public: + void operator++() + { + ++Iterator::m_rowIt; + makeConsistent(); + } + }; + + class RowIterator : public Iterator + { + friend class Table; + RowIterator(Table *table) : Iterator(table) {} + + void makeConsistent() {} + + public: + RowIterator(const RowsIterator rowIt) : Iterator(0) + { + Iterator::m_rowIt = rowIt; + Iterator::m_it = rowIt->second.begin(); + } + + void operator++() { ++Iterator::m_it; } + operator bool() { return Iterator::m_it != Iterator::m_rowIt->second.end(); } + }; + +public: + Table() {} + ~Table() {} + + std::map &rows() { return m_table; } + + Iterator begin() + { + Iterator result(this); + result.m_rowIt = m_table.begin(); + if (result.m_rowIt != m_table.end()) + result.m_it = result.m_rowIt->second.begin(); + return result; + } + + RowIterator rowBegin(const RowKey &row) + { + RowIterator result(this); + result.m_rowIt = m_table.find(row); + if (result.m_rowIt != m_table.end()) + result.m_it = result.m_rowIt->second.begin(); + return result; + } + + ColIterator colBegin(const ColKey &col) + { + ColIterator result(this, col); + result.m_rowIt = m_table.begin(); + result.makeConsistent(); + return result; + } + + Val &value(const RowKey &r, const ColKey &c) + { + return m_table[r][c]; + } + + Iterator insert(const RowKey &r, const ColKey &c, const Val &val) + { + Iterator result(this); + result.m_rowIt = m_table.insert(std::make_pair(r, Row())).first; + result.m_it = result.m_rowIt->second.insert(std::make_pair(c, val)).first; + return result; + } + + Iterator find(const RowKey &r, const ColKey &c) + { + Iterator result(this); + result.m_rowIt = m_table.find(r); + if (result.m_rowIt == m_table.end()) + return; + result.m_it = result.m_rowIt->second.find(c); + if (result.m_it == result.m_rowIt->second.end()) + result.m_rowIt = m_table.end(); + return result; + } + + Iterator erase(const RowKey &r, const ColKey &c) + { + Iterator it(find(r, c)); + return erase(it); + } + + Iterator erase(const Iterator &it) + { + Iterator result(it); + Row &row = it.m_rowIt->second; + ++result.m_it; + row.erase(it.m_it); + if (result.m_it == row.end() && row.empty()) { + result.makeConsistent(); + m_table.erase(it.m_rowIt); + return result; + } + + result.makeConsistent(); + return result; + } + + void erase(const ColKey &c) + { + ColIterator it(colBegin(c)); + while (it) { + RowsIterator rowIt = it.m_rowIt; + rowIt->second.erase(it.m_it); + ++it; + if (rowIt->second.empty()) + m_table.erase(rowIt); + } + } + + void erase(const RowKey &r) + { + m_table.erase(r); + } + + void clear() { m_table.clear(); } +}; + +//-------------------------------------------------------------------------- + +struct LockedResourceP { + TCacheResourceP m_resource; + + LockedResourceP(const TCacheResourceP &resource) : m_resource(resource) + { + m_resource->addLock(); + } + + LockedResourceP(const LockedResourceP &resource) : m_resource(resource.m_resource) + { + m_resource->addLock(); + } + + ~LockedResourceP() + { + m_resource->releaseLock(); + } + + LockedResourceP &operator=(const LockedResourceP &src) + { + src.m_resource->addLock(); + if (m_resource) + m_resource->releaseLock(); + m_resource = src.m_resource; + return *this; + } + + operator bool() const { return m_resource; } + + bool operator<(const LockedResourceP &resource) const { return m_resource < resource.m_resource; } + + TCacheResource *operator->() const { return m_resource.getPointer(); } + TCacheResource &operator*() const { return *m_resource.getPointer(); } +}; + +typedef Table> ResourcesTable; + +//-------------------------------------------------------------------------- + +class TPassiveCacheManager::ResourcesContainer +{ + ResourcesTable m_resources; + +public: + ResourcesContainer() {} + ~ResourcesContainer() {} + + ResourcesTable &getTable() { return m_resources; } +}; + +//***************************************************************************************** +// FxData implementation +//***************************************************************************************** + +TPassiveCacheManager::FxData::FxData() + : m_storageFlag(0), m_passiveCacheId(0) +{ +} + +//------------------------------------------------------------------------- + +TPassiveCacheManager::FxData::~FxData() +{ +} + +//***************************************************************************************** +// Manager generator +//***************************************************************************************** + +//======================================= +// TPassiveCacheManagerGenerator +//--------------------------------------- + +class TPassiveCacheManagerGenerator : public TRenderResourceManagerGenerator +{ + TRenderResourceManager *operator()(void) + { + //return new TPassiveCacheManager; + return TPassiveCacheManager::instance(); + } +}; + +MANAGER_FILESCOPE_DECLARATION_DEP( + TPassiveCacheManager, + TPassiveCacheManagerGenerator, + TFxCacheManager::deps()) + +//***************************************************************************************** +// Implementation +//***************************************************************************************** + +TPassiveCacheManager::TPassiveCacheManager() +#ifdef USE_SQLITE_HDPOOL + : m_currStorageFlag(ON_DISK) +#else + : m_currStorageFlag(IN_MEMORY) +#endif + , + m_enabled(true), m_descriptorCallback(0), m_mutex(QMutex::Recursive), m_resources(new ResourcesContainer) +{ + reset(); +} + +//------------------------------------------------------------------------- + +TPassiveCacheManager::~TPassiveCacheManager() +{ + delete m_resources; +} + +//------------------------------------------------------------------------- + +TPassiveCacheManager *TPassiveCacheManager::instance() +{ + static TPassiveCacheManager theInstance; + return &theInstance; +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::setContextName(unsigned long renderId, const std::string &name) +{ + QMutexLocker locker(&m_mutex); + + //Retrieve the context data if already present + std::map::iterator it = m_contextNames.find(name); + if (it == m_contextNames.end()) + it = m_contextNames.insert(std::make_pair(name, 0)).first; + + it->second = !it->second; + m_contextNamesByRenderId.insert(std::make_pair(renderId, name + "%" + ::toString(it->second))); +} + +//------------------------------------------------------------------------- + +std::string TPassiveCacheManager::getContextName() +{ + QMutexLocker locker(&m_mutex); + + //First, search the context name + std::map::iterator it = + m_contextNamesByRenderId.find(TRenderer::renderId()); + + if (it == m_contextNamesByRenderId.end()) + return ""; + + return it->second; +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +//------------------------------------------------------------------------- + +bool TPassiveCacheManager::isEnabled() const +{ + return m_enabled; +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::setStorageMode(StorageFlag mode) +{ + m_currStorageFlag = mode; +} + +//------------------------------------------------------------------------- + +TPassiveCacheManager::StorageFlag TPassiveCacheManager::getStorageMode() const +{ + return m_currStorageFlag; +} + +//----------------------------------------------------------------------------------- + +void TPassiveCacheManager::setTreeDescriptor(TreeDescriptor callback) +{ + m_descriptorCallback = callback; +} + +//----------------------------------------------------------------------------------- + +int TPassiveCacheManager::getNewPassiveCacheId() +{ + return ++m_currentPassiveCacheId; +} + +//----------------------------------------------------------------------------------- + +int TPassiveCacheManager::updatePassiveCacheId(int id) +{ + if (m_updatingPassiveCacheIds) + m_currentPassiveCacheId = tmax(m_currentPassiveCacheId, id); + else + id = getNewPassiveCacheId(); + + return id; +} + +//----------------------------------------------------------------------------------- + +void TPassiveCacheManager::onSceneLoaded() +{ + m_updatingPassiveCacheIds = false; + +//Initialize the fxs tree description. This was not possible before, as the +//scene was yet incomplete (in loading state). +#ifndef USE_SQLITE_HDPOOL + unsigned int count = m_fxDataVector.size(); + for (unsigned int i = 0; i < count; ++i) { + FxData &data = m_fxDataVector[i]; + (*m_descriptorCallback)(data.m_treeDescription, data.m_fx); + } +#endif +} + +//----------------------------------------------------------------------------------- + +void TPassiveCacheManager::touchFxData(int &idx) +{ + if (idx >= 0) + return; + + QMutexLocker locker(&m_mutex); + + m_fxDataVector.push_back(FxData()); + idx = m_fxDataVector.size() - 1; +} + +//----------------------------------------------------------------------------------- + +int TPassiveCacheManager::declareCached(TFx *fx, int passiveCacheId) +{ + int &idx = fx->getAttributes()->passiveCacheDataIdx(); + touchFxData(idx); + + FxData &data = m_fxDataVector[idx]; + data.m_fx = fx; + data.m_storageFlag = m_currStorageFlag; + data.m_passiveCacheId = updatePassiveCacheId(passiveCacheId); + + return idx; +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::reset() +{ + m_updatingPassiveCacheIds = true; + m_currentPassiveCacheId = 0; + m_fxDataVector.clear(); + m_resources->getTable().clear(); +} + +//------------------------------------------------------------------------- + +bool TPassiveCacheManager::cacheEnabled(TFx *fx) +{ + int idx = fx->getAttributes()->passiveCacheDataIdx(); + if (idx < 0) + return false; + + assert(idx < (int)m_fxDataVector.size()); + + QMutexLocker locker(&m_mutex); + + return m_fxDataVector[idx].m_storageFlag > 0; +} + +//------------------------------------------------------------------------- + +int TPassiveCacheManager::getPassiveCacheId(TFx *fx) +{ + int idx = fx->getAttributes()->passiveCacheDataIdx(); + if (idx < 0) + return 0; + + //This needs not be mutex locked + + assert(idx < (int)m_fxDataVector.size()); + return m_fxDataVector[idx].m_passiveCacheId; +} + +//------------------------------------------------------------------------- + +TPassiveCacheManager::StorageFlag TPassiveCacheManager::getStorageMode(TFx *fx) +{ + int idx = fx->getAttributes()->passiveCacheDataIdx(); + if (idx < 0) + return NONE; + + QMutexLocker locker(&m_mutex); + + return (StorageFlag)m_fxDataVector[idx].m_storageFlag; +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::enableCache(TFx *fx) +{ + int &idx = fx->getAttributes()->passiveCacheDataIdx(); + touchFxData(idx); + + FxData &data = m_fxDataVector[idx]; + + QMutexLocker locker(&m_mutex); + +#ifdef DIAGNOSTICS + DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " + + QString("Enable Cache")); +#endif + + StorageFlag flag = getStorageMode(); + if (flag) { + UCHAR &storedFlag = data.m_storageFlag; + UCHAR oldFlag = storedFlag; + + storedFlag |= flag; + + if (data.m_passiveCacheId == 0) + data.m_passiveCacheId = getNewPassiveCacheId(); + + if ((storedFlag & ON_DISK) && !(oldFlag & ON_DISK)) { + ResourcesTable::ColIterator it = m_resources->getTable().colBegin(data.m_passiveCacheId); + for (; it; ++it) { + std::set &resources = *it; + + std::set::iterator jt; + for (jt = resources.begin(); jt != resources.end(); ++jt) + (*jt)->enableBackup(); + } + } + +#ifndef USE_SQLITE_HDPOOL + if ((storedFlag & IN_MEMORY) && !(oldFlag & IN_MEMORY)) { + data.m_fx = fx; + (*m_descriptorCallback)(data.m_treeDescription, data.m_fx); + } +#endif + } +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::disableCache(TFx *fx) +{ + int idx = fx->getAttributes()->passiveCacheDataIdx(); + if (idx < 0) + return; + + FxData &data = m_fxDataVector[idx]; + + QMutexLocker locker(&m_mutex); + +#ifdef DIAGNOSTICS + DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " + + QString("Disable Cache")); +#endif + + StorageFlag flag = getStorageMode(); + if (flag) { + UCHAR &storedFlag = data.m_storageFlag; + UCHAR oldFlag = storedFlag; + + storedFlag &= ~flag; + + if ((oldFlag & IN_MEMORY) && !(storedFlag & IN_MEMORY)) { + m_resources->getTable().erase(data.m_passiveCacheId); + + data.m_fx = TFxP(); + data.m_treeDescription = ""; + } + +#ifdef USE_SQLITE_HDPOOL + if ((oldFlag & ON_DISK) && !(storedFlag & ON_DISK)) + TCacheResourcePool::instance()->releaseReferences("P" + QString::number(data.m_passiveCacheId)); +#endif + } +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::toggleCache(TFx *fx) +{ + int &idx = fx->getAttributes()->passiveCacheDataIdx(); + touchFxData(idx); + + FxData &data = m_fxDataVector[idx]; + + QMutexLocker locker(&m_mutex); + + StorageFlag flag = getStorageMode(); + if (flag) { + UCHAR &storedFlag = data.m_storageFlag; + UCHAR oldFlag = storedFlag; + + storedFlag ^= flag; + +#ifdef DIAGNOSTICS + DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " + + QString("Toggle Cache (now ") + ((storedFlag & IN_MEMORY) ? "enabled)" : "disabled)")); +#endif + + if (data.m_passiveCacheId == 0) + data.m_passiveCacheId = getNewPassiveCacheId(); + + if ((storedFlag & ON_DISK) && !(oldFlag & ON_DISK)) { + ResourcesTable::ColIterator it = m_resources->getTable().colBegin(data.m_passiveCacheId); + for (; it; ++it) { + std::set &resources = *it; + + std::set::iterator jt; + for (jt = resources.begin(); jt != resources.end(); ++jt) + (*jt)->enableBackup(); + } + } + + //Implementa la versione contraria - eliminazione dell'fx nell'hdPool... + //Metti anche questo in versione ritardata con flush... Il flush e' da unificare... + //e magari da spostare direttamente nell'hdPool + + if ((oldFlag & IN_MEMORY) && !(storedFlag & IN_MEMORY)) { + m_resources->getTable().erase(data.m_passiveCacheId); + + data.m_fx = TFxP(); + data.m_treeDescription = ""; + } + +#ifndef USE_SQLITE_HDPOOL + if ((storedFlag & IN_MEMORY) && !(oldFlag & IN_MEMORY)) { + data.m_fx = fx; + (*m_descriptorCallback)(data.m_treeDescription, data.m_fx); + } +#endif + +#ifdef USE_SQLITE_HDPOOL + if ((oldFlag & ON_DISK) && !(storedFlag & ON_DISK)) + TCacheResourcePool::instance()->releaseReferences("P" + QString::number(data.m_passiveCacheId)); +#endif + } +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::invalidateLevel(const std::string &levelName) +{ + QMutexLocker locker(&m_mutex); + + //Traverse the managed resources for passed levelName. + ResourcesTable &table = m_resources->getTable(); + ResourcesTable::Iterator it = table.begin(); + while (it) { + std::set &resources = *it; + std::set::iterator jt, kt; + for (jt = resources.begin(); jt != resources.end();) { + if ((*jt)->getName().find(levelName) != string::npos) { + kt = jt++; + it->erase(kt); + } else + ++jt; + } + + if (resources.empty()) + it = table.erase(it); + else + ++it; + } + +#ifdef USE_SQLITE_HDPOOL + //Store the level name until the invalidation is forced + m_invalidatedLevels.insert(levelName); +#endif +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::forceInvalidate() +{ +#ifdef USE_SQLITE_HDPOOL + TCacheResourcePool *pool = TCacheResourcePool::instance(); + + //Clear all invalidated levels from the resource pool + std::set::iterator it; + for (it = m_invalidatedLevels.begin(); it != m_invalidatedLevels.end(); ++it) + pool->clearKeyword(*it); + + m_invalidatedLevels.clear(); +#endif +} + +//------------------------------------------------------------------------- + +//Generate the fx's tree description. If it is contained in one of those +//stored with cached fxs, release their associated resources. +void TPassiveCacheManager::onFxChanged(const TFxP &fx) +{ +#ifndef USE_SQLITE_HDPOOL + + std::string fxTreeDescription; + (*m_descriptorCallback)(fxTreeDescription, fx); + + unsigned int count = m_fxDataVector.size(); + for (unsigned int i = 0; i < count; ++i) { + FxData &data = m_fxDataVector[i]; + + if (!data.m_fx) + continue; + + if (data.m_treeDescription.find(fxTreeDescription) != std::string::npos) + m_resources->getTable().erase(data.m_passiveCacheId); + } + +#endif +} + +//------------------------------------------------------------------------- + +//Regenerate the tree descriptions of cached fxs. If the new description does +//not match the previous one, release the associated resources. +void TPassiveCacheManager::onXsheetChanged() +{ +#ifndef USE_SQLITE_HDPOOL + +#ifdef DIAGNOSTICS + DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " XSheet changed"); +#endif + + unsigned int count = m_fxDataVector.size(); + for (unsigned int i = 0; i < count; ++i) { + FxData &data = m_fxDataVector[i]; + + if (!data.m_fx) + continue; + + std::string newTreeDescription; + (*m_descriptorCallback)(newTreeDescription, data.m_fx); + + if (data.m_treeDescription != newTreeDescription) { + m_resources->getTable().erase(data.m_passiveCacheId); + data.m_treeDescription = newTreeDescription; + } + } + +#endif +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::getResource( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData) +{ + if (!(m_enabled && fx && rs.m_userCachable)) + return; + + StorageFlag flag = getStorageMode(fx.getPointer()); + if (flag == NONE) + return; + + std::string contextName(getContextName()); + if (contextName.empty()) + return; + + //Build a resource if none was passed. + if (!resource) + resource = TCacheResourceP(alias, true); + +#ifdef USE_SQLITE_HDPOOL + if (flag & ON_DISK) { + resource->enableBackup(); + + int passiveCacheId = m_fxDataVector[fx->getAttributes()->passiveCacheDataIdx()].m_passiveCacheId; + TCacheResourcePool::instance()->addReference(resource, "P" + QString::number(passiveCacheId)); + } +#endif + + if (flag & IN_MEMORY) { + QMutexLocker locker(&m_mutex); + + int passiveCacheId = m_fxDataVector[fx->getAttributes()->passiveCacheDataIdx()].m_passiveCacheId; + m_resources->getTable().value(contextName, passiveCacheId).insert(resource); + } +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::releaseContextNamesWithPrefix(const std::string &prefix) +{ + QMutexLocker locker(&m_mutex); + +#ifdef DIAGNOSTICS + DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + + " Release Context Name (" + QString::fromStdString(prefix) + ")"); +#endif + + //Retrieve the context range + std::string prefixPlus1 = prefix; + prefixPlus1[prefix.size() - 1]++; + + { + std::map::iterator it, jt; + it = m_contextNames.lower_bound(prefix); + jt = m_contextNames.lower_bound(prefixPlus1); + m_contextNames.erase(it, jt); + } + + //Transfer to temporary + ResourcesTable &table = m_resources->getTable(); + std::map &rows = m_resources->getTable().rows(); + + std::map::iterator it, jt, kt; + it = rows.lower_bound(prefix); + jt = rows.lower_bound(prefixPlus1); + + std::string temporaryName("T"); + for (kt = it; kt != jt; ++kt) { + ResourcesTable::RowIterator lt(kt); + for (; lt; ++lt) + table.value(temporaryName, lt.col()).insert(lt->begin(), lt->end()); + } + rows.erase(it, jt == rows.end() ? rows.lower_bound(prefixPlus1) : jt); +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::releaseOldResources() +{ + QMutexLocker locker(&m_mutex); + + //Release all the resources that were stored in the old render instance of the + //context, PLUS those of the temporary container. + //Resources that were held by the TPassiveCacheManager::getResource() procedure + //are now duplicated in a proper row of the table - and will not be freed. + std::string contextName(getContextName()); + if (contextName.empty()) + return; + + char &lastChar = contextName[contextName.size() - 1]; + lastChar = '0' + !(lastChar - '0'); + + ResourcesTable &table = m_resources->getTable(); + table.erase(contextName); + table.erase("T"); +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::onRenderInstanceStart(unsigned long renderId) +{ + TFxCacheManagerDelegate::onRenderInstanceStart(renderId); + +#ifdef USE_SQLITE_HDPOOL + //Force invalidation of levels before the render starts + forceInvalidate(); +#endif +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::onRenderInstanceEnd(unsigned long renderId) +{ + QMutexLocker locker(&m_mutex); + + releaseOldResources(); + m_contextNamesByRenderId.erase(renderId); +} + +//------------------------------------------------------------------------- + +void TPassiveCacheManager::onRenderStatusEnd(int renderStatus) +{ + if (renderStatus == TRenderer::TESTRUN) + releaseOldResources(); +} diff --git a/toonz/sources/common/tfx/tpredictivecachemanager.cpp b/toonz/sources/common/tfx/tpredictivecachemanager.cpp new file mode 100644 index 0000000..e432dec --- /dev/null +++ b/toonz/sources/common/tfx/tpredictivecachemanager.cpp @@ -0,0 +1,238 @@ + + +#include "trenderer.h" +#include "trasterfx.h" + +#include + +#include "tpredictivecachemanager.h" + +//************************************************************************************************ +// Preliminaries +//************************************************************************************************ + +class TPredictiveCacheManagerGenerator : public TRenderResourceManagerGenerator +{ +public: + TPredictiveCacheManagerGenerator() : TRenderResourceManagerGenerator(true) {} + + TRenderResourceManager *operator()(void) + { + return new TPredictiveCacheManager; + } +}; + +MANAGER_FILESCOPE_DECLARATION_DEP( + TPredictiveCacheManager, + TPredictiveCacheManagerGenerator, + TFxCacheManager::deps()) + +//------------------------------------------------------------------------- + +TPredictiveCacheManager *TPredictiveCacheManager::instance() +{ + return static_cast( + //TPredictiveCacheManager::gen()->getManager(TRenderer::instance()) + TPredictiveCacheManager::gen()->getManager(TRenderer::renderId())); +} + +//************************************************************************************************ +// TPredictiveCacheManager::Imp definition +//************************************************************************************************ + +//======================= +// PredictionData +//----------------------- + +struct PredictionData { + const ResourceDeclaration *m_decl; + int m_usageCount; + + PredictionData(const ResourceDeclaration *declaration) + : m_decl(declaration), m_usageCount(1) {} +}; + +//============================================================================================ + +//===================================== +// TPredictiveCacheManager::Imp +//------------------------------------- + +class TPredictiveCacheManager::Imp +{ +public: + int m_renderStatus; + bool m_enabled; + + std::map m_resources; + QMutex m_mutex; + +public: + //Active getResource(..) callback + typedef void (TPredictiveCacheManager::Imp::*GetResourceFuncPtr)(TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData); + + GetResourceFuncPtr m_getResFuncPtr; + +public: + Imp() + : m_renderStatus(TRenderer::IDLE), m_getResFuncPtr(&Imp::getResourceComputing), m_enabled(TRenderer::instance().isPrecomputingEnabled()) {} + + void getResourceTestRun( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData); + + void getResourceComputing( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData); +}; + +//************************************************************************************************ +// TPredictiveCacheManager methods +//************************************************************************************************ + +TPredictiveCacheManager::TPredictiveCacheManager() + : m_imp(new TPredictiveCacheManager::Imp()) +{ +} + +//--------------------------------------------------------------------------- + +TPredictiveCacheManager::~TPredictiveCacheManager() +{ + delete m_imp; +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::setMaxTileSize(int maxTileSize) +{ +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::setBPP(int bpp) +{ +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::getResource( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData) +{ + if (!m_imp->m_enabled) + return; + + (m_imp->*(m_imp->m_getResFuncPtr))(resource, alias, fx, frame, rs, resData); +} + +//************************************************************************************************ +// Notification-related functions +//************************************************************************************************ + +void TPredictiveCacheManager::Imp::getResourceTestRun( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData) +{ + assert(resData && resData->m_rawData); + if (!(resData && resData->m_rawData)) + //This is a very rare case. I've seen it happen once in a 'pathologic' case + //which involved affines truncation while building aliases. + //The rendering system didn't expect the truncated part 'resurface' in a + //downstream fx with a slightly different affine alias. + + //TODO: Affines should be coded completely in the aliases... in a compact way though. + return; + + if (!resource) + resource = TCacheResourceP(alias, true); + + //Lock against concurrent threads + //QMutexLocker locker(&m_mutex); //preComputing is currently single-threaded + + std::map::iterator it = + m_resources.find(resource); + + if (it != m_resources.end()) + it->second.m_usageCount++; + else { + //Already initializes usageCount at 1 + m_resources.insert(std::make_pair(resource, PredictionData(resData))).first; + } +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::Imp::getResourceComputing( + TCacheResourceP &resource, const string &alias, + const TFxP &fx, double frame, const TRenderSettings &rs, + ResourceDeclaration *resData) +{ + //If there is no declaration data, either the request can be resolved in one + //computation code (therefore it is uninteresting for us), or it was never declared. + //Anyway, return. + if (!resData) + return; + + //NO! The refCount is dynamically depleted - could become 0 from n... + //assert(!(resData->m_tiles.size() == 1 && resData->m_tiles[0].m_refCount == 1)); + + if (!resource) + resource = TCacheResourceP(alias); + + if (!resource) + return; + + //Lock against concurrent threads + QMutexLocker locker(&m_mutex); + + std::map::iterator it = + m_resources.find(resource); + + if (it == m_resources.end()) + return; + + if (--it->second.m_usageCount <= 0) + m_resources.erase(it); +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::onRenderStatusStart(int renderStatus) +{ + m_imp->m_renderStatus = renderStatus; + switch (renderStatus) { + case TRenderer::TESTRUN: + m_imp->m_getResFuncPtr = &TPredictiveCacheManager::Imp::getResourceTestRun; + CASE TRenderer::COMPUTING : m_imp->m_getResFuncPtr = &TPredictiveCacheManager::Imp::getResourceComputing; + } +} + +//--------------------------------------------------------------------------- + +void TPredictiveCacheManager::onRenderStatusEnd(int renderStatus) +{ + switch (renderStatus) { + case TRenderer::TESTRUN: + + //All resources which have just 1 computation tile, which is also referenced + //only once, are released. + + std::map::iterator it; + for (it = m_imp->m_resources.begin(); it != m_imp->m_resources.end();) { + const ResourceDeclaration *decl = it->second.m_decl; + + if (decl->m_tiles.size() == 1 && decl->m_tiles[0].m_refCount == 1) { + std::map::iterator jt = it++; + m_imp->m_resources.erase(jt); + } else + it++; + } + } +} diff --git a/toonz/sources/common/tfx/trenderer.cpp b/toonz/sources/common/tfx/trenderer.cpp new file mode 100644 index 0000000..e61c5fc --- /dev/null +++ b/toonz/sources/common/tfx/trenderer.cpp @@ -0,0 +1,1595 @@ + + +#include "trenderer.h" +#include "trendererP.h" + +// TnzCore includes +#include "tsystem.h" +#include "tthreadmessage.h" +#include "tthread.h" +#include "tconvert.h" +#include "tstream.h" +#include "trasterimage.h" +#include "trop.h" +#include "timagecache.h" +#include "tstopwatch.h" + +// TnzBase includes +#include "trenderresourcemanager.h" +#include "tpredictivecachemanager.h" + +// Qt includes +#include +#include +#include +#include +#include +#include + +// tcg includes +#include "tcg/tcg_deleter_types.h" + +//Debug +//#define DIAGNOSTICS +//#include "diagnostics.h" + +#include +#include + +using namespace TThread; + +std::vector calculateSortedFxs(TRasterFxP rootFx); + +//================================================================================ +// Preliminaries - Anonymous namespace +//================================================================================ + +namespace +{ +//TRenderer-thread association storage. It provides TRenderers the per-thread +//singleton status from inside a rendering process. +QThreadStorage rendererStorage; + +//Same for render process ids. +QThreadStorage renderIdsStorage; + +//------------------------------------------------------------------------------- + +//Interlacing functions for field-based rendering +inline void interlace(TRasterP f0, const TRasterP &f1, int field) +{ + if (f0->getPixelSize() != f1->getPixelSize()) + throw TException("interlace: invalid raster combination"); + + assert(f0->getBounds() == f1->getBounds()); + + int outWrapBytes = f0->getWrap() * f0->getPixelSize(); + int inWrapBytes = f1->getWrap() * f1->getPixelSize(); + int outWrapBytes2 = outWrapBytes * 2; + int inWrapBytes2 = inWrapBytes * 2; + int rowSize = f0->getLx() * f0->getPixelSize(); + + f1->lock(); + f0->lock(); + UCHAR *rowIn = f1->getRawData(); + UCHAR *rowOut = f0->getRawData(); + + if (field) + rowIn += inWrapBytes; + + int count = f0->getLy() / 2; + while (--count) { + memcpy(rowOut, rowIn, rowSize); + rowIn += inWrapBytes2; + rowOut += outWrapBytes2; + } + + f1->unlock(); + f0->unlock(); +} +} // anonymous namespace + +//================================================================================ +// Preliminaries - output rasters management +//================================================================================ + +//=================== +// RasterItem +//------------------- + +//! The RasterItem class represents a reusable output raster for rendering purposes. +//! RasterItems are used as TRenderer's output rasters in order to avoid the overhead +//! of allocating one raster for each rendered frame. +//! Each frame-rendering task will lock a RasterItem as preallocated output before starting the +//! render, therefore changing the \b RasterItem::m_busy attribute accordingly. +//! As each frame is rendered on a separate thread, the number of RasterItems that +//! TRenderer will allocate depends on the number of rendering threads specified to it. + +//! \sa RasterPool, TRenderer classes + +class RasterItem +{ + string m_rasterId; + +public: + int m_bpp; + bool m_busy; + + //--------------------------------------------------------- + + RasterItem(const TDimension &size, int bpp, bool busyFlag) + : m_rasterId(""), m_bpp(bpp), m_busy(busyFlag) + { + TRasterP raster; + if (bpp == 32) + raster = TRaster32P(size); + else if (bpp == 64) + raster = TRaster64P(size); + else + assert(false); + + m_rasterId = TImageCache::instance()->getUniqueId(); + TImageCache::instance()->add(m_rasterId, TRasterImageP(raster)); + } + + //--------------------------------------------------------- + + ~RasterItem() + { + TImageCache::instance()->remove(m_rasterId); + } + + //--------------------------------------------------------- + + TRasterP getRaster() const + { + TRasterImageP rimg = (TRasterImageP)TImageCache::instance()->get(m_rasterId, true); + return rimg ? rimg->getRaster() : TRasterP(); + } + +private: + //not implemented + RasterItem(); + RasterItem(const TRaster &RasterItem); +}; + +//================================================================================ + +//=================== +// RasterPool +//------------------- + +//! Stores a list of RasterItems under TRenderer's requests. +class RasterPool +{ + TDimension m_size; + int m_bpp; + + typedef std::list RasterRepository; + RasterRepository m_rasterRepository; + + TThread::Mutex m_repositoryLock; + +public: + RasterPool() : m_size(-1, -1) {} + ~RasterPool(); + + void setRasterSpecs(const TDimension &size, int bpp); + void getRasterSpecs(TDimension &size, int &bpp) + { + size = m_size; + bpp = m_bpp; + } + + TRasterP getRaster(); + TRasterP getRaster(const TDimension &size, int bpp); + + void releaseRaster(const TRasterP &r); + + void clear(); +}; + +//--------------------------------------------------------- + +void RasterPool::clear() +{ + QMutexLocker sl(&m_repositoryLock); + clearPointerContainer(m_rasterRepository); +} + +//--------------------------------------------------------- + +void RasterPool::setRasterSpecs(const TDimension &size, int bpp) +{ + if (size != m_size || bpp != m_bpp) { + m_size = size; + m_bpp = bpp; + clear(); + } +} + +//--------------------------------------------------------- + +TRasterP RasterPool::getRaster(const TDimension &size, int bpp) +{ + assert(size == m_size && bpp == m_bpp); + return getRaster(); +} + +//--------------------------------------------------------- + +//!Returns for the first not-busy raster +TRasterP RasterPool::getRaster() +{ + QMutexLocker sl(&m_repositoryLock); + + RasterRepository::iterator it = m_rasterRepository.begin(); + while (it != m_rasterRepository.end()) { + RasterItem *rasItem = *it; + if (rasItem->m_busy == false) { + TRasterP raster = rasItem->getRaster(); + + if (!raster) { + delete rasItem; + m_rasterRepository.erase(it++); + continue; + } + + rasItem->m_busy = true; + raster->clear(); + return raster; + } + + ++it; + } + + RasterItem *rasItem = new RasterItem(m_size, m_bpp, true); + m_rasterRepository.push_back(rasItem); + + return rasItem->getRaster(); +} + +//--------------------------------------------------------- + +//!Cerca il raster \b r in m_rasterRepository; se lo trova setta a \b false il campo \b m_busy. +void RasterPool::releaseRaster(const TRasterP &r) +{ + if (!r) + return; + + QMutexLocker sl(&m_repositoryLock); + for (RasterRepository::iterator it = m_rasterRepository.begin(); + it != m_rasterRepository.end(); + ++it) { + RasterItem *rasItem = *it; + if (rasItem->getRaster()->getRawData() == r->getRawData()) { + assert(rasItem->m_busy); + rasItem->m_busy = false; + return; + } + } +} + +//--------------------------------------------------------- + +RasterPool::~RasterPool() +{ + /*if (m_rasterRepository.size()) + TSystem::outputDebug("~RasterPool: itemCount = " + toString ((int)m_rasterRepository.size())+" (should be 0)\n");*/ + + //Release all raster items + clear(); +} + +//================================================================================ +// Internal rendering classes declaration +//================================================================================ + +//===================== +// TRendererImp +//--------------------- + +class TRendererImp : public TSmartObject +{ +public: + struct RenderInstanceInfos { + int m_canceled; + int m_activeTasks; + int m_status; + + RenderInstanceInfos() + : m_canceled(false), m_activeTasks(0), m_status(TRenderer::IDLE) {} + }; + +public: + typedef std::vector PortContainer; + typedef PortContainer::iterator PortContainerIterator; + + QReadWriteLock m_portsLock; + PortContainer m_ports; + + QMutex m_renderInstancesMutex; + std::map m_activeInstances; + + //! The renderer Id is a number uniquely associated with a TRenderer instance. + static unsigned long m_rendererIdCounter; + + //! The render Id is a number uniquely associated with a render process started + //! with the startRendering() method. + static unsigned long m_renderIdCounter; + + unsigned long m_rendererId; + + Executor m_executor; + + bool m_precomputingEnabled; + RasterPool m_rasterPool; + + std::vector m_managers; + + TAtomicVar m_undoneTasks; + //std::vector m_waitingLoops; + std::vector m_waitingLoops; + + TRasterFxP rootFx; + + //------------------------------------------------------------- + + TRendererImp(int nThreads); + ~TRendererImp(); + + void startRendering(unsigned long renderId, const std::vector &renderDatas); + + void notifyRasterStarted(const TRenderPort::RenderData &rd); + void notifyRasterCompleted(const TRenderPort::RenderData &rd); + void notifyRasterFailure(const TRenderPort::RenderData &rd, TException &e); + void notifyRenderFinished(bool isCanceled = false); + + void addPort(TRenderPort *port); + void removePort(TRenderPort *port); + + void abortRendering(unsigned long renderId); + void stopRendering(bool waitForCompleteStop); + + bool hasToDie(unsigned long renderId); + int getRenderStatus(unsigned long renderId); + + void enablePrecomputing(bool on) { m_precomputingEnabled = on; } + bool isPrecomputingEnabled() const { return m_precomputingEnabled; } + + void setThreadsCount(int nThreads) { m_executor.setMaxActiveTasks(nThreads); } + + inline void declareRenderStart(unsigned long renderId); + inline void declareRenderEnd(unsigned long renderId); + inline void declareFrameStart(double frame); + inline void declareFrameEnd(double frame); + inline void declareStatusStart(int renderStatus); + inline void declareStatusEnd(int renderStatus); + + void quitWaitingLoops(); +}; + +#ifdef WIN32 +template class DVAPI TSmartPointerT; +#endif + +typedef TSmartPointerT TRendererImpP; + +unsigned long TRendererImp::m_rendererIdCounter = 0; +unsigned long TRendererImp::m_renderIdCounter = 0; + +//================================================================================ + +//=================== +// RenderTask +//------------------- + +class RenderTask : public TThread::Runnable +{ + std::vector m_frames; + + unsigned long m_taskId; + unsigned long m_renderId; + + TRendererImpP m_rendererImp; + + TFxPair m_fx; + TPointD m_framePos; + TDimension m_frameSize; + TRenderSettings m_info; + + bool m_fieldRender, m_stereoscopic; + + Mutex m_rasterGuard; + TTile m_tileA; //in normal and field rendering, Rendered at given frame; in stereoscopic, rendered left frame + TTile m_tileB; //in field rendering, rendered at frame + 0.5; in stereoscopic, rendered right frame + +public: + RenderTask(unsigned long renderId, unsigned long taskId, + double frame, const TRenderSettings &ri, const TFxPair &fx, + const TPointD &framePos, const TDimension &frameSize, + const TRendererImpP &rendererImp); + + ~RenderTask() {} + + void addFrame(double frame) { m_frames.push_back(frame); } + + void buildTile(TTile &tile); + void releaseTiles(); + + void onFrameStarted(); + void onFrameCompleted(); + void onFrameFailed(TException &e); + + void preRun(); + void run(); + + int taskLoad() { return 100; } + + void onFinished(TThread::RunnableP); +}; + +//================================================================================ +// Implementations +//================================================================================ + +//================== +// TRenderer +//------------------ + +TRenderer::TRenderer(int nThread) +{ + m_imp = new TRendererImp(nThread); //Already adds a ref +} + +//--------------------------------------------------------- + +TRenderer::TRenderer(TRendererImp *imp) + : m_imp(imp) +{ + if (m_imp) + m_imp->addRef(); +} + +//--------------------------------------------------------- + +TRenderer::TRenderer(const TRenderer &r) + : m_imp(r.m_imp) +{ + if (m_imp) + m_imp->addRef(); +} + +//--------------------------------------------------------- + +void TRenderer::operator=(const TRenderer &r) +{ + m_imp = r.m_imp; + if (m_imp) + m_imp->addRef(); +} + +//--------------------------------------------------------- + +TRenderer::~TRenderer() +{ + if (m_imp) + m_imp->release(); +} + +//--------------------------------------------------------- + +//! The static method is intended for use inside a rendering thread only. It returns +//! a copy of the eventual TRenderer interface installed on the thread - +//! or an empty TRenderer if none happened to be. It can be set using the +//! install() and uninstall() methods. +TRenderer TRenderer::instance() +{ + TRendererImp **lData = rendererStorage.localData(); + if (lData) + return TRenderer(*lData); + return 0; +} + +//--------------------------------------------------------- + +//! Returns the renderer id. +unsigned long TRenderer::rendererId() +{ + return m_imp->m_rendererId; +} + +//--------------------------------------------------------- + +//! Returns the rendering process id currently running on the invoking +//! thread. +unsigned long TRenderer::renderId() +{ + unsigned long *lData = renderIdsStorage.localData(); + return lData ? *lData : -1; +} + +//--------------------------------------------------------- + +//! Builds and returns an id for starting a new rendering process. +unsigned long TRenderer::buildRenderId() +{ + return TRendererImp::m_renderIdCounter++; +} + +//--------------------------------------------------------- + +//! Returns the renderId that will be used by the next startRendering() call. +//! This method can be used to retrieve the renderId of a rendering instance +//! before it is actually started - provided that no other render instance +//! is launched inbetween. +unsigned long TRenderer::nextRenderId() +{ + return TRendererImp::m_renderIdCounter; +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareRenderStart(unsigned long renderId) +{ + //Inform the resource managers + for (unsigned int i = 0; i < m_managers.size(); ++i) + m_managers[i]->onRenderInstanceStart(renderId); +} + +//! Declares that the render process of passed renderId is about to start. +void TRenderer::declareRenderStart(unsigned long renderId) +{ + m_imp->declareRenderStart(renderId); +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareRenderEnd(unsigned long renderId) +{ + //Inform the resource managers + for (int i = m_managers.size() - 1; i >= 0; --i) + m_managers[i]->onRenderInstanceEnd(renderId); +} + +//! Declares that the render process of passed renderId has ended. +void TRenderer::declareRenderEnd(unsigned long renderId) +{ + m_imp->declareRenderEnd(renderId); +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareFrameStart(double frame) +{ + //Inform the resource managers + for (unsigned int i = 0; i < m_managers.size(); ++i) + m_managers[i]->onRenderFrameStart(frame); +} + +//! Declares that render of passed frame is about to start. +void TRenderer::declareFrameStart(double frame) +{ + m_imp->declareFrameStart(frame); +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareFrameEnd(double frame) +{ + //Inform the resource managers + for (int i = m_managers.size() - 1; i >= 0; --i) + m_managers[i]->onRenderFrameEnd(frame); +} + +//! Declares that render of passed has ended. +void TRenderer::declareFrameEnd(double frame) +{ + m_imp->declareFrameEnd(frame); +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareStatusStart(int renderStatus) +{ + //Inform the resource managers + for (unsigned int i = 0; i < m_managers.size(); ++i) + m_managers[i]->onRenderStatusStart(renderStatus); +} + +//--------------------------------------------------------- + +inline void TRendererImp::declareStatusEnd(int renderStatus) +{ + //Inform the resource managers + for (int i = m_managers.size() - 1; i >= 0; --i) + m_managers[i]->onRenderStatusEnd(renderStatus); +} + +//--------------------------------------------------------- + +//! Installs the specified render process on the invoking thread. +void TRenderer::install(unsigned long renderId) +{ + m_imp->addRef(); + rendererStorage.setLocalData(new (TRendererImp *)(m_imp)); + renderIdsStorage.setLocalData(new unsigned long(renderId)); +} + +//--------------------------------------------------------- + +//! Uninstalls any rendering process active on the invoking thread. +void TRenderer::uninstall() +{ + rendererStorage.setLocalData(0); + renderIdsStorage.setLocalData(0); + m_imp->release(); +} + +//--------------------------------------------------------- + +TRenderResourceManager *TRenderer::getManager(unsigned int id) const +{ + return m_imp->m_managers[id]; +} + +//--------------------------------------------------------- + +void TRenderer::enablePrecomputing(bool on) +{ + m_imp->enablePrecomputing(on); +} + +//--------------------------------------------------------- + +bool TRenderer::isPrecomputingEnabled() const +{ + return m_imp->isPrecomputingEnabled(); +} + +//--------------------------------------------------------- + +void TRenderer::setThreadsCount(int nThreads) +{ + m_imp->setThreadsCount(nThreads); +} + +//--------------------------------------------------------- + +void TRenderer::addPort(TRenderPort *port) +{ + m_imp->addPort(port); +} + +//--------------------------------------------------------- + +void TRenderer::removePort(TRenderPort *port) +{ + m_imp->removePort(port); +} + +//--------------------------------------------------------- + +unsigned long TRenderer::startRendering( + double f, + const TRenderSettings &info, + const TFxPair &actualRoot) +{ + assert(f >= 0); + + std::vector *rds = new std::vector; + rds->push_back(RenderData(f, info, actualRoot)); + return startRendering(rds); +} + +//--------------------------------------------------------- + +//! Queues a rendering event in the main event loop. +//! NOTE: The passed pointer is owned by the TRenderer after it is submitted for rendering - +//! do not delete it later. +unsigned long TRenderer::startRendering(const std::vector *renderDatas) +{ + if (renderDatas->empty()) { + delete renderDatas; + return -1; + } + + //Build a new render Id + unsigned long renderId = m_imp->m_renderIdCounter++; + + TRendererStartInvoker::StartInvokerRenderData srd; + srd.m_renderId = renderId; + srd.m_renderDataVector = renderDatas; + TRendererStartInvoker::instance()->emitStartRender(m_imp, srd); + + return renderId; +} + +//--------------------------------------------------------- + +void TRenderer::abortRendering(unsigned long renderId) +{ + m_imp->abortRendering(renderId); +} + +//--------------------------------------------------------- + +void TRenderer::stopRendering(bool waitForCompleteStop) +{ + m_imp->stopRendering(waitForCompleteStop); +} + +//--------------------------------------------------------- + +bool TRenderer::isAborted(unsigned long renderId) const +{ + return m_imp->hasToDie(renderId); +} + +//--------------------------------------------------------- + +int TRenderer::getRenderStatus(unsigned long renderId) const +{ + return m_imp->getRenderStatus(renderId); +} + +//================================================================================ + +//===================== +// TRendererImp +//--------------------- + +TRendererImp::TRendererImp(int nThreads) + : m_executor(), m_undoneTasks(), m_rendererId(m_rendererIdCounter++), m_precomputingEnabled(true) +{ + m_executor.setMaxActiveTasks(nThreads); + + std::vector &generators = + TRenderResourceManagerGenerator::generators(false); + + //May be adopted by other TRenderers from now on. + addRef(); + + rendererStorage.setLocalData(new (TRendererImp *)(this)); + + unsigned int i; + for (i = 0; i < generators.size(); ++i) { + TRenderResourceManager *manager = (*generators[i])(); + if (manager) + m_managers.push_back(manager); + } + + rendererStorage.setLocalData(0); +} + +//--------------------------------------------------------- + +TRendererImp::~TRendererImp() +{ + rendererStorage.setLocalData(new (TRendererImp *)(this)); + + int i; + for (i = m_managers.size() - 1; i >= 0; --i) + if (m_managers[i]->renderHasOwnership()) + delete m_managers[i]; + + rendererStorage.setLocalData(0); +} + +//--------------------------------------------------------- + +void TRendererImp::addPort(TRenderPort *port) +{ + QWriteLocker sl(&m_portsLock); + + PortContainerIterator it = std::find(m_ports.begin(), m_ports.end(), port); + if (it == m_ports.end()) + m_ports.push_back(port); +} + +//--------------------------------------------------------- + +void TRendererImp::removePort(TRenderPort *port) +{ + QWriteLocker sl(&m_portsLock); + + PortContainerIterator it = std::find(m_ports.begin(), m_ports.end(), port); + if (it != m_ports.end()) + m_ports.erase(it); +} + +//--------------------------------------------------------- + +bool TRendererImp::hasToDie(unsigned long renderId) +{ + QMutexLocker sl(&m_renderInstancesMutex); + + std::map::iterator it = m_activeInstances.find(renderId); + assert(it != m_activeInstances.end()); + return it == m_activeInstances.end() ? true : it->second.m_canceled; +} + +//--------------------------------------------------------- + +int TRendererImp::getRenderStatus(unsigned long renderId) +{ + QMutexLocker sl(&m_renderInstancesMutex); + + std::map::iterator it = m_activeInstances.find(renderId); + assert(it != m_activeInstances.end()); + return it == m_activeInstances.end() ? true : it->second.m_status; +} + +//--------------------------------------------------------- + +void TRendererImp::abortRendering(unsigned long renderId) +{ + QMutexLocker sl(&m_renderInstancesMutex); + + std::map::iterator it = m_activeInstances.find(renderId); + if (it != m_activeInstances.end()) + it->second.m_canceled = true; +} + +//--------------------------------------------------------- + +void TRendererImp::stopRendering(bool waitForCompleteStop) +{ + QMutexLocker sl(&m_renderInstancesMutex); + + { + //Tasks already stop rendering on their own when they don't find their render ids here. + std::map::iterator it; + for (it = m_activeInstances.begin(); it != m_activeInstances.end(); ++it) + it->second.m_canceled = true; + } + + if (waitForCompleteStop && m_undoneTasks > 0) { + //Sometimes, QEventLoop suddenly stops processing slots (especially those notifications + //from active rendering instances) - therefore resulting in a block of the application. + //I've not figured out why (#QTBUG-11649?) - but substituting with a plain while + //seems to do the trick... + + /*QEventLoop eventLoop; + m_waitingLoops.push_back(&eventLoop); + eventLoop.exec();*/ + + bool loopQuit = false; + m_waitingLoops.push_back(&loopQuit); + + sl.unlock(); + + while (!loopQuit) + QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); + } +} + +//--------------------------------------------------------- + +void TRendererImp::quitWaitingLoops() +{ + //Make the stopRendering waiting loops quit + while (!m_waitingLoops.empty()) { + //rendererImp->m_waitingLoops.back()->quit(); + *m_waitingLoops.back() = true; + m_waitingLoops.pop_back(); + } +} + +//--------------------------------------------------------- + +void TRendererImp::notifyRasterStarted(const TRenderPort::RenderData &rd) +{ + //Since notifications may trigger port removals, we always work on a copy of the ports + //vector. + TRendererImp::PortContainer portsCopy; + { + QReadLocker sl(&m_portsLock); + portsCopy = m_ports; + } + + for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) + (*it)->onRenderRasterStarted(rd); +} + +//--------------------------------------------------------- + +void TRendererImp::notifyRasterCompleted(const TRenderPort::RenderData &rd) +{ + TRendererImp::PortContainer portsCopy; + { + QReadLocker sl(&m_portsLock); + portsCopy = m_ports; + } + + assert(rd.m_rasA); + + for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) + (*it)->onRenderRasterCompleted(rd); +} + +//--------------------------------------------------------- + +void TRendererImp::notifyRasterFailure(const TRenderPort::RenderData &rd, TException &e) +{ + TRendererImp::PortContainer portsCopy; + { + QReadLocker sl(&m_portsLock); + portsCopy = m_ports; + } + + for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) + (*it)->onRenderFailure(rd, e); +} + +//--------------------------------------------------------- + +void TRendererImp::notifyRenderFinished(bool isCanceled) +{ + TRendererImp::PortContainer portsCopy; + { + QReadLocker sl(&m_portsLock); + portsCopy = m_ports; + } + + auto sortedFxs = calculateSortedFxs(rootFx); + for (auto fx : sortedFxs) { + if (fx) + const_cast(fx)->callEndRenderHandler(); + } + + for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) + (*it)->onRenderFinished(); +} + +//================================================================================ + +//==================== +// TRenderPort +//-------------------- + +TRenderPort::TRenderPort() +{ +} + +//--------------------------------------------------------- + +TRenderPort::~TRenderPort() +{ +} + +//--------------------------------------------------------- + +//!Setta \b m_renderArea a \b area e pulisce l'istanza corrente di \b RasterPool. +void TRenderPort::setRenderArea(const TRectD &area) +{ + m_renderArea = area; +} + +//--------------------------------------------------------- + +//!Ritorna \b m_renderArea. +TRectD &TRenderPort::getRenderArea() +{ + return m_renderArea; +} + +//================================================================================ + +//=================== +// RenderTask +//------------------- + +RenderTask::RenderTask(unsigned long renderId, unsigned long taskId, + double frame, const TRenderSettings &ri, const TFxPair &fx, + const TPointD &framePos, const TDimension &frameSize, + const TRendererImpP &rendererImp) + : m_renderId(renderId), m_taskId(taskId), m_info(ri), m_fx(fx), m_frameSize(frameSize), m_framePos(framePos), m_rendererImp(rendererImp), m_fieldRender(ri.m_fieldPrevalence != TRenderSettings::NoField), m_stereoscopic(ri.m_stereoscopic) +{ + m_frames.push_back(frame); + + //Connect the onFinished slot + connect(this, SIGNAL(finished(TThread::RunnableP)), this, SLOT(onFinished(TThread::RunnableP))); + connect(this, SIGNAL(exception(TThread::RunnableP)), this, SLOT(onFinished(TThread::RunnableP))); + + //The shrink info is currently reversed to the settings'affine. Shrink info in the TRenderSettings + //is no longer supported. + m_info.m_shrinkX = m_info.m_shrinkY = 1; +} + +//--------------------------------------------------------- + +void RenderTask::preRun() +{ + TRectD geom(m_framePos, TDimensionD(m_frameSize.lx, m_frameSize.ly)); + + if (m_fx.m_frameA) + m_fx.m_frameA->dryCompute(geom, m_frames[0], m_info); + + if (m_fx.m_frameB) + m_fx.m_frameB->dryCompute(geom, m_fieldRender ? m_frames[0] + 0.5 : m_frames[0], m_info); +} + +//--------------------------------------------------------- + +void RenderTask::run() +{ + //Retrieve the task's frame + assert(!m_frames.empty()); + double t = m_frames[0]; + + if (m_rendererImp->hasToDie(m_renderId)) { + TException e("Render task aborted"); + onFrameFailed(e); + return; + } + + //Install the renderer in current thread + rendererStorage.setLocalData(new (TRendererImp *)(m_rendererImp.getPointer())); + renderIdsStorage.setLocalData(new unsigned long(m_renderId)); + + //Inform the managers of frame start + m_rendererImp->declareFrameStart(t); + + auto sortedFxs = calculateSortedFxs(m_fx.m_frameA); + for (auto fx : sortedFxs) { + if (fx) + const_cast(fx)->callStartRenderFrameHandler(&m_info, t); + } + + try { + onFrameStarted(); + + TStopWatch::global(8).start(); + + if (!m_fieldRender && !m_stereoscopic) { + //Common case - just build the first tile + buildTile(m_tileA); + /*-- 通常はここがFxのレンダリング処理 --*/ + m_fx.m_frameA->compute(m_tileA, t, m_info); + } else { + assert(!(m_stereoscopic && m_fieldRender)); + //Field rendering or stereoscopic case + if (m_stereoscopic) { + buildTile(m_tileA); + m_fx.m_frameA->compute(m_tileA, t, m_info); + + buildTile(m_tileB); + m_fx.m_frameB->compute(m_tileB, t, m_info); + } + //if fieldPrevalence, Decide the rendering frames depending on field prevalence + else if (m_info.m_fieldPrevalence == TRenderSettings::EvenField) { + buildTile(m_tileA); + m_fx.m_frameA->compute(m_tileA, t, m_info); + + buildTile(m_tileB); + m_fx.m_frameB->compute(m_tileB, t + 0.5, m_info); + } else { + buildTile(m_tileB); + m_fx.m_frameA->compute(m_tileB, t, m_info); + + buildTile(m_tileA); + m_fx.m_frameB->compute(m_tileA, t + 0.5, m_info); + } + } + + TStopWatch::global(8).stop(); + + onFrameCompleted(); + } catch (TException &e) { + onFrameFailed(e); + } catch (...) { + TException ex("Unknown render exception"); + onFrameFailed(ex); + } + + //Inform the managers of frame end + m_rendererImp->declareFrameEnd(t); + + //Uninstall the renderer from current thread + rendererStorage.setLocalData(0); + renderIdsStorage.setLocalData(0); + + for (auto fx : sortedFxs) { + if (fx) + const_cast(fx)->callEndRenderFrameHandler(&m_info, t); + } +} + +//--------------------------------------------------------- + +void RenderTask::buildTile(TTile &tile) +{ + tile.m_pos = m_framePos; + tile.setRaster(m_rendererImp->m_rasterPool.getRaster(m_frameSize, m_info.m_bpp)); +} + +//--------------------------------------------------------- + +void RenderTask::releaseTiles() +{ + m_rendererImp->m_rasterPool.releaseRaster(m_tileA.getRaster()); + m_tileA.setRaster(TRasterP()); + if (m_fieldRender || m_stereoscopic) { + m_rendererImp->m_rasterPool.releaseRaster(m_tileB.getRaster()); + m_tileB.setRaster(TRasterP()); + } +} + +//--------------------------------------------------------- + +void RenderTask::onFrameStarted() +{ + TRenderPort::RenderData rd(m_frames, m_info, 0, 0, m_renderId, m_taskId); + m_rendererImp->notifyRasterStarted(rd); +} + +//--------------------------------------------------------- + +void RenderTask::onFrameCompleted() +{ + TRasterP rasA(m_tileA.getRaster()); + TRasterP rasB(m_tileB.getRaster()); + + if (m_fieldRender) { + assert(rasB); + + double t = m_frames[0]; + + int f = (m_info.m_fieldPrevalence == TRenderSettings::EvenField) ? 0 : 1; + interlace(rasA, rasB, f); + rasB = TRasterP(); + } + + TRenderPort::RenderData rd(m_frames, m_info, rasA, rasB, m_renderId, m_taskId); + m_rendererImp->notifyRasterCompleted(rd); +} + +//--------------------------------------------------------- + +void RenderTask::onFrameFailed(TException &e) +{ + //TRasterP evenRas(m_evenTile.getRaster()); + + TRenderPort::RenderData rd(m_frames, m_info, m_tileA.getRaster(), m_tileB.getRaster(), m_renderId, m_taskId); + m_rendererImp->notifyRasterFailure(rd, e); +} + +//--------------------------------------------------------- + +void RenderTask::onFinished(TThread::RunnableP) +{ + TRendererImp *rendererImp = m_rendererImp.getPointer(); + --rendererImp->m_undoneTasks; + + //Tiles release back to the Raster Pool happens in the main thread, after all possible + //signals emitted in the onFrameCompleted/Failed notifications have been resolved, thus + //ensuring that no other rendering thread owns the rasters before them. + releaseTiles(); + + //Update the render instance status + bool instanceExpires = false; + { + QMutexLocker sl(&rendererImp->m_renderInstancesMutex); + std::map::iterator it = + rendererImp->m_activeInstances.find(m_renderId); + + if (it != rendererImp->m_activeInstances.end() && (--it->second.m_activeTasks) <= 0) { + instanceExpires = true; + rendererImp->m_activeInstances.erase(m_renderId); + } + } + + //If the render instance has just expired + if (instanceExpires) { + /*-- キャンセルされた場合はm_overallRenderedRegionの更新をしない --*/ + bool isCanceled = (m_info.m_isCanceled && *m_info.m_isCanceled); + + //Inform the render ports + rendererImp->notifyRenderFinished(isCanceled); + + //NOTE: This slot is currently invoked on the main thread. It could eventually be + //invoked directly on rendering threads, specifying the Qt::DirectConnection option - + //but probably there would be no real advantage in doing so... + + //Temporarily install the renderer in current thread + rendererStorage.setLocalData(new (TRendererImp *)(rendererImp)); + renderIdsStorage.setLocalData(new unsigned long(m_renderId)); + + //Inform the resource managers + rendererImp->declareRenderEnd(m_renderId); + + //Uninstall the temporary + rendererStorage.setLocalData(0); + renderIdsStorage.setLocalData(0); + + rendererImp->m_rasterPool.clear(); // Isn't this misplaced? Should be in the block + } // below... + + //If no rendering task (of this or other render instances) is found... + if (rendererImp->m_undoneTasks == 0) { + QMutexLocker sl(&rendererImp->m_renderInstancesMutex); + rendererImp->quitWaitingLoops(); + } +} + +//================================================================================ +// Tough Stuff +//================================================================================ + +void TRendererStartInvoker::emitStartRender( + TRendererImp *renderer, StartInvokerRenderData rd) +{ + renderer->addRef(); + Q_EMIT startRender(renderer, rd); +} + +//--------------------------------------------------------- + +void TRendererStartInvoker::doStartRender( + TRendererImp *renderer, StartInvokerRenderData rd) +{ + renderer->startRendering(rd.m_renderId, *rd.m_renderDataVector); + renderer->release(); + delete rd.m_renderDataVector; +} + +std::vector calculateSortedFxs(TRasterFxP rootFx) +{ + std::map> E; /* 辺の情報 */ + std::set Sources; /* 入次数0のノード群 */ + + std::queue Q; + Q.push(rootFx.getPointer()); + + E[rootFx.getPointer()] = std::set(); + + while (!Q.empty()) { + const TFx *vptr = Q.front(); + Q.pop(); + if (!vptr) { + continue; + } + + /* 繋がっている入力ポートの先の Fx を訪問する + 入力ポートが無ければ終了 */ + int portCount = vptr->getInputPortCount(); + if (portCount < 1) { + Sources.insert(vptr); + continue; + } + for (int i = 0; i < portCount; i++) { + TFxPort *port = vptr->getInputPort(i); + if (!port) { + continue; + } + TFxP u = port->getFx(); + const TFx *uptr = u.getPointer(); + if (E.count(uptr) == 0) { + E[uptr] = std::set(); + } + if (E[uptr].count(vptr) == 0) { + E[uptr].insert(vptr); + } + Q.push(uptr); + } + } + + /* トポロジカルソート */ + std::set visited; + std::vector L; + std::function visit = [&visit, &visited, &E, &L](const TFx *fx) { + if (visited.count(fx)) + return; + visited.insert(fx); + auto edge = E[fx]; + for (auto i = edge.cbegin(); i != edge.cend(); i++) { + visit(*i); + } + L.insert(L.begin(), fx); + }; + for (auto i = E.cbegin(); i != E.cend(); i++) { + visit(i->first); + } + return L; +} + +//--------------------------------------------------------- + +void TRendererImp::startRendering(unsigned long renderId, const vector &renderDatas) +{ + rootFx = renderDatas.front().m_fxRoot.m_frameA; + int T = renderDatas.size(); + for (int z = 0; z < T; z++) { + auto sortedFxs = calculateSortedFxs(renderDatas[z].m_fxRoot.m_frameA); + if (z == 0) { + for (auto fx : sortedFxs) { + if (fx) + const_cast(fx)->callStartRenderHandler(); + } + } + } + + struct locals { + + static inline void setStorage(TRendererImp *imp, unsigned long renderId) + { + rendererStorage.setLocalData(new (TRendererImp *)(imp)); + renderIdsStorage.setLocalData(new unsigned long(renderId)); + } + + static inline void clearStorage() + { + rendererStorage.setLocalData(0); + renderIdsStorage.setLocalData(0); + } + + static inline void declareStatusStart( + TRendererImp *imp, TRenderer::RenderStatus status, + RenderInstanceInfos *renderInfos) + { + renderInfos->m_status = status; + imp->declareStatusStart(status); + } + + //----------------------------------------------------------------------- + + struct InstanceDeclaration { + TRendererImp *m_imp; + unsigned long m_renderId; + bool m_rollback; + + InstanceDeclaration(TRendererImp *imp, unsigned long renderId) + : m_imp(imp), m_renderId(renderId), m_rollback(true) {} + + ~InstanceDeclaration() + { + if (m_rollback) { + QMutexLocker locker(&m_imp->m_renderInstancesMutex); + + m_imp->m_activeInstances.erase(m_renderId); + if (m_imp->m_undoneTasks == 0) + m_imp->quitWaitingLoops(); + } + } + + void commit() { m_rollback = false; } + }; + + struct StorageDeclaration { + StorageDeclaration(TRendererImp *imp, unsigned long renderId) + { + setStorage(imp, renderId); + } + + ~StorageDeclaration() + { + clearStorage(); + } + }; + + struct RenderDeclaration { + TRendererImp *m_imp; + unsigned long m_renderId; + bool m_rollback; + + RenderDeclaration(TRendererImp *imp, unsigned long renderId) + : m_imp(imp), m_renderId(renderId), m_rollback(true) + { + imp->declareRenderStart(renderId); + } + + ~RenderDeclaration() + { + if (m_rollback) + m_imp->declareRenderEnd(m_renderId); + } + + void commit() { m_rollback = false; } + }; + + struct StatusDeclaration { + TRendererImp *m_imp; + TRenderer::RenderStatus m_status; + + StatusDeclaration(TRendererImp *imp, TRenderer::RenderStatus status, + RenderInstanceInfos *renderInfos) + : m_imp(imp), m_status(status) + { + declareStatusStart(imp, status, renderInfos); + } + + ~StatusDeclaration() + { + m_imp->declareStatusEnd(m_status); + } + }; + }; // locals + + //DIAGNOSTICS_CLEAR; + + //---------------------------------------------------------------------- + // Preliminary initializations + //---------------------------------------------------------------------- + + // Calculate the overall render area - sum of all render ports' areas + TRectD renderArea; + { + QReadLocker sl(&m_portsLock); + + for (PortContainerIterator it = m_ports.begin(); it != m_ports.end(); ++it) + renderArea += (*it)->getRenderArea(); + } + + const TRenderSettings &info(renderDatas[0].m_info); + + //Extract the render geometry + TPointD pos(renderArea.getP00()); + TDimension frameSize(tceil(renderArea.getLx()), tceil(renderArea.getLy())); + + TRectD camBox(TPointD(pos.x / info.m_shrinkX, pos.y / info.m_shrinkY), + TDimensionD(frameSize.lx, frameSize.ly)); + + //Refresh the raster pool specs + m_rasterPool.setRasterSpecs(frameSize, info.m_bpp); + + //Set a temporary active instance count - so that hasToDie(renderId) returns false + RenderInstanceInfos *renderInfos; + { + QMutexLocker locker(&m_renderInstancesMutex); + renderInfos = &m_activeInstances[renderId]; + renderInfos->m_activeTasks = 1; + } + + locals::InstanceDeclaration instanceDecl(this, renderId); + + //---------------------------------------------------------------------- + // Clustering - Render Tasks creation + //---------------------------------------------------------------------- + + std::vector tasksVector; + + struct TasksCleaner { + std::vector &m_tasksVector; + ~TasksCleaner() + { + std::for_each(m_tasksVector.begin(), m_tasksVector.end(), + tcg::deleter()); + } + } tasksCleaner = {tasksVector}; + + unsigned long tasksIdCounter = 0; + + std::map clusters; + std::vector::const_iterator it; + std::map::iterator jt; + + for (it = renderDatas.begin(); it != renderDatas.end(); ++it) { + // Check for user cancels + if (hasToDie(renderId)) + return; + + // Build the frame's description alias + const TRenderer::RenderData &renderData = *it; + + /*--- カメラサイズ (LevelAutoやノイズで使用する) ---*/ + TRenderSettings rs = renderData.m_info; + rs.m_cameraBox = camBox; + /*--- 途中でPreview計算がキャンセルされたときのフラグ ---*/ + rs.m_isCanceled = &renderInfos->m_canceled; + + TRasterFxP fx = renderData.m_fxRoot.m_frameA; + assert(fx); + + double frame = renderData.m_frame; + + string alias = fx->getAlias(frame, renderData.m_info); + if (renderData.m_fxRoot.m_frameB) + alias = alias + renderData.m_fxRoot.m_frameB->getAlias(frame, renderData.m_info); + + // Search the alias among stored clusters - and store the frame + jt = clusters.find(alias); + + if (jt == clusters.end()) { + RenderTask *newTask = new RenderTask( + renderId, tasksIdCounter++, + renderData.m_frame, rs, renderData.m_fxRoot, + pos, frameSize, this); + + tasksVector.push_back(newTask); + clusters.insert(std::make_pair(alias, newTask)); + } else + jt->second->addFrame(renderData.m_frame); + + // Call processEvents to make the GUI reactive. + QCoreApplication::instance()->processEvents(); + } + + // Release the clusters - we'll just need the tasks vector from now on + clusters.clear(); + + std::vector::iterator kt, kEnd = tasksVector.end(); + { + // Install TRenderer on current thread before proceeding + locals::StorageDeclaration storageDecl(this, renderId); + + // Inform the resource managers + locals::RenderDeclaration renderDecl(this, renderId); + + //---------------------------------------------------------------------- + // Precomputing + //---------------------------------------------------------------------- + + if (m_precomputingEnabled) { + //Set current maxTileSize for cache manager precomputation + const TRenderSettings &rs = renderDatas[0].m_info; + TPredictiveCacheManager::instance()->setMaxTileSize(rs.m_maxTileSize); + TPredictiveCacheManager::instance()->setBPP(rs.m_bpp); + + //Perform the first precomputing run - fx usages declaration + { + locals::StatusDeclaration firstrunDecl(this, TRenderer::FIRSTRUN, renderInfos); + + for (kt = tasksVector.begin(); kt != kEnd; ++kt) { + if (hasToDie(renderId)) + return; + + (*kt)->preRun(); + + //NOTE: Thread-specific data must be temporarily uninstalled before + //processing events (which may redefine the thread data). + locals::clearStorage(); + QCoreApplication::instance()->processEvents(); + locals::setStorage(this, renderId); + } + } + + //Pass to the TESTRUN status - this one should faithfully reproduce + //the actual COMPUTING status + { + locals::StatusDeclaration testrunDecl(this, TRenderer::TESTRUN, renderInfos); + + for (kt = tasksVector.begin(); kt != kEnd; ++kt) { + if (hasToDie(renderId)) + return; + + (*kt)->preRun(); + + //NOTE: Thread-specific data must be temporarily uninstalled before + //processing events (which may redefine the thread data). + locals::clearStorage(); + QCoreApplication::instance()->processEvents(); + locals::setStorage(this, renderId); + } + } + } + + //---------------------------------------------------------------------- + // Render + //---------------------------------------------------------------------- + + locals::declareStatusStart(this, TRenderer::COMPUTING, renderInfos); + + // Update the tasks counts + m_undoneTasks += tasksVector.size(); + { + QMutexLocker locker(&m_renderInstancesMutex); + renderInfos->m_activeTasks = tasksVector.size(); + } + + renderDecl.commit(); // Declarations are taken over by render tasks + } + + instanceDecl.commit(); // Same here + + // Launch the render + for (kt = tasksVector.begin(); kt != tasksVector.end(); ++kt) + m_executor.addTask(*kt); + + tasksVector.clear(); // Prevent tasks destruction by TasksCleaner +} + +void TRenderer::initialize() +{ + TRendererStartInvoker::instance(); +} diff --git a/toonz/sources/common/tfx/trendererP.h b/toonz/sources/common/tfx/trendererP.h new file mode 100644 index 0000000..de555b5 --- /dev/null +++ b/toonz/sources/common/tfx/trendererP.h @@ -0,0 +1,62 @@ + + +#ifndef TRENDERERP_INCLUDE +#define TRENDERERP_INCLUDE + +#include +#include + +#include "trenderer.h" + +//============================================================================================= + +//! This is a private class used to convey the TRenderer::startRendering +//! methods into Qt queued slots. This is necessary since these methods implicitly +//! perform event processing, which could cause trouble in case they are invoked from +//! events which must respect a strict ordering. +//! \n \n +//! For example, suppose that a render must be invoked upon a mousePressEvent, +//! and that such event must have been completely processed before the corrispondant +//! mouseReleaseEvent is invoked - calling the startRendering method *directly* by +//! the mousePressEvent may cause the mouseReleaseEvent to be processed before the +//! former's end. +class TRendererStartInvoker : public QObject +{ + Q_OBJECT + +public: + struct StartInvokerRenderData { + unsigned long m_renderId; + const RenderDataVector *m_renderDataVector; + }; + +public: + TRendererStartInvoker() + { + qRegisterMetaType("StartInvokerRenderData"); + + connect(this, SIGNAL(startRender(TRendererImp *, StartInvokerRenderData)), + this, SLOT(doStartRender(TRendererImp *, StartInvokerRenderData)), + Qt::QueuedConnection); + } + ~TRendererStartInvoker() {} + + static TRendererStartInvoker *instance() + { + static TRendererStartInvoker theInstance; + return &theInstance; + } + + void emitStartRender(TRendererImp *renderer, StartInvokerRenderData rd); + + Q_SIGNALS : + + void + startRender(TRendererImp *, StartInvokerRenderData); + +public Q_SLOTS: + + void doStartRender(TRendererImp *, StartInvokerRenderData rd); +}; + +#endif //TRENDERERP_INCLUDE diff --git a/toonz/sources/common/tfx/trenderresourcemanager.cpp b/toonz/sources/common/tfx/trenderresourcemanager.cpp new file mode 100644 index 0000000..0f341ad --- /dev/null +++ b/toonz/sources/common/tfx/trenderresourcemanager.cpp @@ -0,0 +1,263 @@ + + +#include "trenderresourcemanager.h" +#include "trenderer.h" + +//*********************************************************************************************** +// Resource managers builder for instance-scoped resource managers +//*********************************************************************************************** + +/* +This manager class is used to create and destroy instance-scope managers through the +renderStart/renderEnd notifications. Observe that this involves maintenance of a container +structure of the active renderIds against resource managers. +*/ + +class RenderInstanceManagersBuilder : public TRenderResourceManager +{ + T_RENDER_RESOURCE_MANAGER + + typedef std::vector ManagersVector; + std::map m_managersMap; + +public: + RenderInstanceManagersBuilder() {} + ~RenderInstanceManagersBuilder() {} + + static RenderInstanceManagersBuilder *instance(); + + TRenderResourceManager *getManager(unsigned long renderId, unsigned int idx) const; + + void onRenderInstanceStart(unsigned long id); + void onRenderInstanceEnd(unsigned long id); + + bool renderHasOwnership() { return false; } +}; + +//=============================================================================================== + +class RenderInstanceManagersBuilderGenerator : public TRenderResourceManagerGenerator +{ +public: + TRenderResourceManager *operator()(void) + { + return RenderInstanceManagersBuilder::instance(); + } +}; + +MANAGER_FILESCOPE_DECLARATION(RenderInstanceManagersBuilder, RenderInstanceManagersBuilderGenerator); + +//*********************************************************************************************** +// Stub managers and generators +//*********************************************************************************************** + +/* +These manager-generator stubs are substitutes used to maintain dependency order about managers +which have render instance scope. They retrieve ordered event calls that are passed to the +dedicated instanceScope handler. +*/ + +class InstanceResourceManagerStub : public TRenderResourceManager +{ + TRenderResourceManagerGenerator *m_generator; + +public: + InstanceResourceManagerStub(TRenderResourceManagerGenerator *generator) + : m_generator(generator) + { + } + + void onRenderInstanceStart(unsigned long id); + void onRenderInstanceEnd(unsigned long id); + + void onRenderFrameStart(double f); + void onRenderFrameEnd(double f); + + virtual void onRenderStatusStart(int renderStatus); + virtual void onRenderStatusEnd(int renderStatus); +}; + +//=============================================================================================== + +class StubGenerator : public TRenderResourceManagerGenerator +{ + TRenderResourceManagerGenerator *m_generator; + +public: + StubGenerator(TRenderResourceManagerGenerator *generator) + : m_generator(generator) + { + } + + TRenderResourceManager *operator()() + { + return new InstanceResourceManagerStub(m_generator); + } +}; + +//*********************************************************************************************** +// TRenderResourceManagerGenerator methods +//*********************************************************************************************** + +std::vector &TRenderResourceManagerGenerator::generators() +{ + static std::vector generatorsInstance; + return generatorsInstance; +} + +//------------------------------------------------------------------------- + +std::vector &TRenderResourceManagerGenerator::generators(bool instanceScope) +{ + static std::vector generatorsInstance; + static std::vector generatorsRenderer; + return instanceScope ? generatorsInstance : generatorsRenderer; +} + +//=============================================================================================== + +TRenderResourceManagerGenerator::TRenderResourceManagerGenerator(bool renderInstanceScope) + : m_instanceScope(renderInstanceScope) +{ + //In case this has a renderInstanceScope, build a stub generator + if (renderInstanceScope) { + RenderInstanceManagersBuilder::gen(); //Stubs depend on this manager + + static std::vector stubGenerators; + stubGenerators.push_back(new StubGenerator(this)); + } + + generators().push_back(this); + + std::vector &scopeGenerators = + generators(renderInstanceScope); + + scopeGenerators.push_back(this); + m_managerIndex = scopeGenerators.size() - 1; +} + +//------------------------------------------------------------------------- + +TRenderResourceManager *TRenderResourceManagerGenerator::getManager(const TRenderer &renderer) const +{ + return m_instanceScope ? 0 : renderer.getManager(m_managerIndex); +} + +//------------------------------------------------------------------------- + +TRenderResourceManager *TRenderResourceManagerGenerator::getManager(unsigned long renderId) const +{ + return m_instanceScope ? RenderInstanceManagersBuilder::instance()->getManager(renderId, m_managerIndex) : 0; +} + +//*********************************************************************************************** +// "Instance-scoped Managers" - Management methods +//*********************************************************************************************** + +RenderInstanceManagersBuilder *RenderInstanceManagersBuilder::instance() +{ + static RenderInstanceManagersBuilder theInstance; + return &theInstance; +} + +//------------------------------------------------------------------------- + +inline TRenderResourceManager *RenderInstanceManagersBuilder:: + getManager(unsigned long renderId, unsigned int idx) const +{ + std::map::const_iterator it = m_managersMap.find(renderId); + return it == m_managersMap.end() ? 0 : it->second[idx]; +} + +//------------------------------------------------------------------------- + +void RenderInstanceManagersBuilder::onRenderInstanceStart(unsigned long id) +{ + //Build the instance managers + std::map::iterator it = + m_managersMap.insert(std::make_pair(id, ManagersVector())).first; + + std::vector &instanceScopeGenerators = + TRenderResourceManagerGenerator::generators(true); + + unsigned int i; + for (i = 0; i < instanceScopeGenerators.size(); ++i) + it->second.push_back((*instanceScopeGenerators[i])()); +} + +//------------------------------------------------------------------------- + +void RenderInstanceManagersBuilder::onRenderInstanceEnd(unsigned long id) +{ + //Delete the instance managers + std::map::iterator it = + m_managersMap.find(id); + + assert(it != m_managersMap.end()); + + unsigned int i; + for (i = 0; i < it->second.size(); ++i) { + if (it->second[i]->renderHasOwnership()) + delete it->second[i]; + } + + m_managersMap.erase(it); +} + +//=============================================================================================== + +void InstanceResourceManagerStub::onRenderInstanceStart(unsigned long id) +{ + RenderInstanceManagersBuilder::instance()->getManager(id, + m_generator->getGeneratorIndex()) + ->onRenderInstanceStart(id); +} + +//------------------------------------------------------------------------- + +void InstanceResourceManagerStub::onRenderInstanceEnd(unsigned long id) +{ + RenderInstanceManagersBuilder::instance()->getManager(id, + m_generator->getGeneratorIndex()) + ->onRenderInstanceEnd(id); +} + +//------------------------------------------------------------------------- + +void InstanceResourceManagerStub::onRenderFrameStart(double f) +{ + RenderInstanceManagersBuilder::instance()->getManager( + TRenderer::renderId(), + m_generator->getGeneratorIndex()) + ->onRenderFrameStart(f); +} + +//------------------------------------------------------------------------- + +void InstanceResourceManagerStub::onRenderFrameEnd(double f) +{ + RenderInstanceManagersBuilder::instance()->getManager( + TRenderer::renderId(), + m_generator->getGeneratorIndex()) + ->onRenderFrameEnd(f); +} + +//------------------------------------------------------------------------- + +void InstanceResourceManagerStub::onRenderStatusStart(int renderStatus) +{ + RenderInstanceManagersBuilder::instance()->getManager( + TRenderer::renderId(), + m_generator->getGeneratorIndex()) + ->onRenderStatusStart(renderStatus); +} + +//------------------------------------------------------------------------- + +void InstanceResourceManagerStub::onRenderStatusEnd(int renderStatus) +{ + RenderInstanceManagersBuilder::instance()->getManager( + TRenderer::renderId(), + m_generator->getGeneratorIndex()) + ->onRenderStatusEnd(renderStatus); +} diff --git a/toonz/sources/common/tfx/ttzpimagefx.cpp b/toonz/sources/common/tfx/ttzpimagefx.cpp new file mode 100644 index 0000000..e9c16b8 --- /dev/null +++ b/toonz/sources/common/tfx/ttzpimagefx.cpp @@ -0,0 +1,280 @@ + + +// TnzBase includes +#include "trasterfx.h" + +#include "ttzpimagefx.h" + +//********************************************************************************************** +// Global functions +//********************************************************************************************** + +void parseIndexes(string indexes, vector &items) +{ +#ifndef MACOSX + char seps[] = " ,;"; + char *token; + if (indexes == "all" || indexes == "All" || indexes == "ALL") + indexes = "0-4095"; + char *context = 0; + token = strtok_s((char *)(indexes.c_str()), seps, &context); + while (token != NULL) { + items.push_back(token); + token = strtok_s(NULL, seps, &context); + } +#else + char seps[] = " ,;"; + char *token; + if (indexes == "all" || indexes == "All" || indexes == "ALL") + indexes = "0-4095"; + token = strtok((char *)(indexes.c_str()), seps); + while (token != NULL) { + items.push_back(token); + token = strtok(NULL, seps); + } +#endif +} + +//------------------------------------------------------------------- + +void insertIndexes(vector items, PaletteFilterFxRenderData *t) +{ +#ifndef MACOSX + for (int i = 0; i < (int)items.size(); i++) { + char *starttoken, *endtoken; + char subseps[] = "-"; + string tmp = items[i]; + char *context = 0; + starttoken = strtok_s((char *)tmp.c_str(), subseps, &context); + endtoken = strtok_s(NULL, subseps, &context); + if (!endtoken && isInt(starttoken)) { + int index; + index = toInt(starttoken); + t->m_colors.insert(index); + } else { + if (isInt(starttoken) && isInt(endtoken)) { + int start, end; + start = toInt(starttoken); + end = toInt(endtoken); + for (int i = start; i <= end; i++) + t->m_colors.insert(i); + } + } + } +#else + for (int i = 0; i < (int)items.size(); i++) { + char *starttoken, *endtoken; + char subseps[] = "-"; + string tmp = items[i]; + starttoken = strtok((char *)tmp.c_str(), subseps); + endtoken = strtok(NULL, subseps); + if (!endtoken && isInt(starttoken)) { + int index; + index = toInt(starttoken); + t->m_colors.insert(index); + } else { + if (isInt(starttoken) && isInt(endtoken)) { + int start, end; + start = toInt(starttoken); + end = toInt(endtoken); + for (int i = start; i <= end; i++) + t->m_colors.insert(i); + } + } + } +#endif +} + +//********************************************************************************************** +// ExternalPaletteFxRenderData implementation +//********************************************************************************************** + +ExternalPaletteFxRenderData::ExternalPaletteFxRenderData(TPaletteP palette, const string &name) + : m_palette(palette), m_name(name) +{ +} + +//------------------------------------------------------------------------------ + +bool ExternalPaletteFxRenderData::operator==(const TRasterFxRenderData &data) const +{ + return false; // non sono capace. non ho i pezzi per browsare nell'albero da qui. +} + +//------------------------------------------------------------------------------ + +string ExternalPaletteFxRenderData::toString() const +{ + return m_name; +} + +//********************************************************************************************** +// PaletteFilterFxRenderData implementation +//********************************************************************************************** + +PaletteFilterFxRenderData::PaletteFilterFxRenderData() +{ + m_keep = false; + m_type = eApplyToInksAndPaints; +} + +//------------------------------------------------------------------------------ + +bool PaletteFilterFxRenderData::operator==(const TRasterFxRenderData &data) const +{ + const PaletteFilterFxRenderData *theData = dynamic_cast(&data); + if (!theData) + return false; + + return (theData->m_colors == m_colors && theData->m_type == m_type && theData->m_keep == m_keep); +} + +//------------------------------------------------------------------------------ + +string PaletteFilterFxRenderData::toString() const +{ + string alias; + std::set::const_iterator it = m_colors.begin(); + for (; it != m_colors.end(); ++it) + alias += ::toString(*it); + alias += "keep=" + ::toString((int)m_keep); + alias += "type=" + ::toString(m_type); + return alias; +} + +//********************************************************************************************** +// SandorFxRenderData implementation +//********************************************************************************************** + +SandorFxRenderData::SandorFxRenderData(Type type, int argc, const char *argv[], int border, int shrink, + const TRectD &controllerBBox, const TRasterP &controller) + : m_type(type), m_border(border), m_shrink(shrink), m_blendParams(), m_callParams(), m_contourParams(), m_argc(argc), m_controllerBBox(controllerBBox), m_controller(controller), m_controllerAlias() +{ + for (int i = 0; i < argc; i++) + m_argv[i] = argv[i]; +} + +//------------------------------------------------------------------------------ + +bool SandorFxRenderData::operator==(const TRasterFxRenderData &data) const +{ + const SandorFxRenderData *theData = dynamic_cast(&data); + if (!theData) + return false; + + if (m_type == BlendTz) + return (theData->m_blendParams.m_colorIndex == m_blendParams.m_colorIndex && + theData->m_blendParams.m_noBlending == m_blendParams.m_noBlending && + theData->m_blendParams.m_amount == m_blendParams.m_amount && + theData->m_blendParams.m_smoothness == m_blendParams.m_smoothness); + + if (m_type == Calligraphic || m_type == OutBorder) + return (theData->m_callParams.m_colorIndex == m_callParams.m_colorIndex && + theData->m_callParams.m_thickness == m_callParams.m_thickness && + theData->m_callParams.m_doWDiagonal == m_callParams.m_doWDiagonal && + theData->m_callParams.m_noise == m_callParams.m_noise && + theData->m_callParams.m_horizontal == m_callParams.m_horizontal && + theData->m_callParams.m_vertical == m_callParams.m_vertical && + theData->m_callParams.m_accuracy == m_callParams.m_accuracy && + theData->m_callParams.m_upWDiagonal == m_callParams.m_upWDiagonal); + + if (m_type == ArtAtContour) + return (theData->m_contourParams.m_density == m_contourParams.m_density && + theData->m_contourParams.m_colorIndex == m_contourParams.m_colorIndex && + theData->m_contourParams.m_keepLine == m_contourParams.m_keepLine && + theData->m_contourParams.m_maxOrientation == m_contourParams.m_maxOrientation && + theData->m_contourParams.m_maxDistance == m_contourParams.m_maxDistance && + theData->m_contourParams.m_maxSize == m_contourParams.m_maxSize && + theData->m_contourParams.m_minOrientation == m_contourParams.m_minOrientation && + theData->m_contourParams.m_minDistance == m_contourParams.m_minDistance && + theData->m_contourParams.m_minSize == m_contourParams.m_minSize && + theData->m_contourParams.m_randomness == m_contourParams.m_randomness && + theData->m_contourParams.m_keepColor == m_contourParams.m_keepColor && + theData->m_contourParams.m_includeAlpha == m_contourParams.m_includeAlpha && + theData->m_controllerAlias == m_controllerAlias); + + return false; +} + +//------------------------------------------------------------------------------ + +string SandorFxRenderData::toString() const +{ + string alias; + if (m_type == BlendTz) { + alias += ::toString(m_blendParams.m_colorIndex) + " "; + alias += ::toString(m_blendParams.m_smoothness) + " "; + alias += ::toString(m_blendParams.m_amount) + " "; + alias += ::toString(m_blendParams.m_noBlending); + return alias; + } + + if (m_type == Calligraphic || m_type == OutBorder) { + alias += ::toString(m_callParams.m_colorIndex) + " "; + alias += ::toString(m_callParams.m_noise) + " "; + alias += ::toString(m_callParams.m_accuracy) + " "; + alias += ::toString(m_callParams.m_upWDiagonal) + " "; + alias += ::toString(m_callParams.m_vertical) + " "; + alias += ::toString(m_callParams.m_upWDiagonal) + " "; + alias += ::toString(m_callParams.m_horizontal) + " "; + alias += ::toString(m_callParams.m_thickness); + return alias; + } + + if (m_type == ArtAtContour) { + alias += ::toString(m_contourParams.m_maxSize) + " "; + alias += ::toString(m_contourParams.m_minSize) + " "; + alias += ::toString(m_contourParams.m_maxOrientation) + " "; + alias += ::toString(m_contourParams.m_minOrientation) + " "; + alias += ::toString(m_contourParams.m_randomness) + " "; + alias += ::toString(m_contourParams.m_maxDistance) + " "; + alias += ::toString(m_contourParams.m_minDistance) + " "; + alias += ::toString(m_contourParams.m_density) + " "; + alias += ::toString(m_contourParams.m_keepLine) + " "; + alias += ::toString(m_contourParams.m_keepColor) + " "; + alias += ::toString(m_contourParams.m_includeAlpha) + " "; + alias += ::toString(m_contourParams.m_colorIndex) + " "; + alias += m_controllerAlias; + return alias; + } + + return alias; +} + +//------------------------------------------------------------------------------ + +TRectD SandorFxRenderData::getBBoxEnlargement(const TRectD &bbox) +{ + switch (m_type) { + case BlendTz: { + //Nothing happen, unless we have color 0 among the blended ones. In such case, + //we have to enlarge the bbox proportionally to the amount param. + vector items; + string indexes = std::string(m_argv[0]); + parseIndexes(indexes, items); + PaletteFilterFxRenderData paletteFilterData; + insertIndexes(items, &paletteFilterData); + + if (paletteFilterData.m_colors.size() > 0 && *paletteFilterData.m_colors.begin() == 0) + return bbox.enlarge(m_blendParams.m_amount); + + return bbox; + } + + CASE Calligraphic : __OR OutBorder: + { + return bbox.enlarge(m_callParams.m_thickness); + } + + CASE ArtAtContour: + { + return bbox.enlarge( + tmax(tceil(m_controllerBBox.getLx()), tceil(m_controllerBBox.getLy())) * m_contourParams.m_maxSize); + } + + DEFAULT : { + assert(false); + return bbox; + } + } +} diff --git a/toonz/sources/common/tfx/unaryFx.cpp b/toonz/sources/common/tfx/unaryFx.cpp new file mode 100644 index 0000000..8420ff1 --- /dev/null +++ b/toonz/sources/common/tfx/unaryFx.cpp @@ -0,0 +1,247 @@ + + +#include "tbasefx.h" +#include "trop.h" +#include "tdoubleparam.h" +#include "tnotanimatableparam.h" +#include "trasterfx.h" +#include "tflash.h" +#include "tfxparam.h" + +//#define ALLOW_SHEAR + +//============================================================================== + +TGeometryFx::TGeometryFx() +{ + setName(L"Geometry"); +} + +//-------------------------------------------------- + +void TGeometryFx::compute(TFlash &flash, int frame) +{ + flash.multMatrix(getPlacement(frame)); + TRasterFx::compute(flash, frame); +} + +//--------------------------------------------------------------- + +void TGeometryFx::doCompute( + TTile &tile, + double frame, + const TRenderSettings &ri) +{ + TRasterFxPort *input = dynamic_cast(getInputPort(0)); + assert(input); + + if (!input->isConnected()) + return; + + if (!getActiveTimeRegion().contains(frame)) { + TRasterFxP(input->getFx())->compute(tile, frame, ri); + return; + } + + if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster())) + throw TException("AffineFx unsupported pixel type"); + + TAffine aff1 = getPlacement(frame); + TRenderSettings ri2(ri); + ri2.m_affine = ri2.m_affine * aff1; + + TRasterFxP src = getInputPort("source")->getFx(); + src->compute(tile, frame, ri2); + return; +} + +//-------------------------------------------------- + +bool TGeometryFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) +{ + TRasterFxPort *input = dynamic_cast(getInputPort(0)); + assert(input); + + if (input->isConnected()) { + TRasterFxP fx = input->getFx(); + assert(fx); + bool ret = fx->doGetBBox(frame, bBox, info); + if (getActiveTimeRegion().contains(frame)) + bBox = getPlacement(frame) * bBox; + return ret; + } else { + bBox = TRectD(); + return false; + } + return true; +}; + +//-------------------------------------------------- + +string TGeometryFx::getAlias(double frame, const TRenderSettings &info) const +{ + TGeometryFx *tthis = const_cast(this); + TAffine affine = tthis->getPlacement(frame); + + string alias = getFxType(); + alias += "["; + + // alias degli effetti connessi alle porte di input separati da virgole + // una porta non connessa da luogo a un alias vuoto (stringa vuota) + + for (int i = 0; i < getInputPortCount(); ++i) { + TFxPort *port = getInputPort(i); + if (port->isConnected()) { + TRasterFxP ifx = port->getFx(); + assert(ifx); + alias += ifx->getAlias(frame, info); + } + alias += ","; + } + + return alias + + (areAlmostEqual(affine.a11, 0) ? "0" : ::toString(affine.a11, 5)) + "," + + (areAlmostEqual(affine.a12, 0) ? "0" : ::toString(affine.a12, 5)) + "," + + (areAlmostEqual(affine.a13, 0) ? "0" : ::toString(affine.a13, 5)) + "," + + (areAlmostEqual(affine.a21, 0) ? "0" : ::toString(affine.a21, 5)) + "," + + (areAlmostEqual(affine.a22, 0) ? "0" : ::toString(affine.a22, 5)) + "," + + (areAlmostEqual(affine.a23, 0) ? "0" : ::toString(affine.a23, 5)) + "]"; +} + +//-------------------------------------------------- + +void TGeometryFx::transform(double frame, + int port, + const TRectD &rectOnOutput, + const TRenderSettings &infoOnOutput, + TRectD &rectOnInput, + TRenderSettings &infoOnInput) +{ + rectOnInput = rectOnOutput; + TAffine aff = getPlacement(frame); + + infoOnInput = infoOnOutput; + infoOnInput.m_affine = infoOnInput.m_affine * aff; +} + +//================================================== + +NaAffineFx::NaAffineFx(bool isDpiAffine) + : m_aff(TAffine()), m_isDpiAffine(isDpiAffine) +{ + addInputPort("source", m_port); + setName(L"Geometry-NaAffineFx"); +} + +//-------------------------------------------------- + +TFx *NaAffineFx::clone(bool recursive) const +{ + NaAffineFx *clonedFx = dynamic_cast(TFx::clone(recursive)); + assert(clonedFx); + clonedFx->m_aff = m_aff; + clonedFx->m_isDpiAffine = m_isDpiAffine; + return clonedFx; +} + +//-------------------------------------------------- + +void NaAffineFx::compute(TFlash &flash, int frame) +{ + TGeometryFx::compute(flash, frame); +} + +//================================================== + +void TRasterFx::compute(TFlash &flash, int frame) +{ + for (int i = getInputPortCount() - 1; i >= 0; i--) { + TFxPort *port = getInputPort(i); + + if (port->isConnected() && !port->isaControlPort()) { + flash.pushMatrix(); + ((TRasterFxP)(port->getFx()))->compute(flash, frame); + flash.popMatrix(); + } + } +} + +//-------------------------------------------------- + +FX_IDENTIFIER_IS_HIDDEN(NaAffineFx, "naAffineFx") + +//================================================================== +// Geometric Fx +//================================================================== + +//================================================================== + +//================================================================== + +//------------------------------------------------------------------------------ + +class InvertFx : public TBaseRasterFx +{ + FX_DECLARATION(InvertFx) + TRasterFxPort m_input; + TBoolParamP m_redChan, m_greenChan, m_blueChan, m_alphaChan; + +public: + InvertFx() : m_redChan(true), m_greenChan(true), m_blueChan(true), m_alphaChan(false) + { + addInputPort("source", m_input); + // bindParam(this, "red_channel" , m_redChan); + // bindParam(this, "green_channel", m_greenChan); + // bindParam(this, "blue_channel" , m_blueChan); + // bindParam(this, "alpha_channel", m_alphaChan); + setName(L"InvertFx"); + }; + + ~InvertFx(){}; + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) + { + if (m_input.isConnected()) { + bool ret = m_input->doGetBBox(frame, bBox, info); + return ret; + } else { + bBox = TRectD(); + return false; + } + }; + + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) + { + if (!m_input.isConnected()) + return; + + m_input->compute(tile, frame, ri); + + TRop::invert(tile.getRaster(), m_redChan->getValue(), m_greenChan->getValue(), m_blueChan->getValue(), m_alphaChan->getValue()); + } +}; + +//================================================================== +// Video Fx +//================================================================== + +//================================================================== + +//=== + +// Geometric +//FX_IDENTIFIER(ScaleFx, "scaleFx") +//FX_IDENTIFIER(MoveFx, "moveFx") +//FX_IDENTIFIER(AffineFx, "affineFx") +//FX_IDENTIFIER(CropFx, "cropFx") + +// Color +FX_IDENTIFIER(InvertFx, "invertFx") + +// Video +//FX_IDENTIFIER(FieldFx, "fieldFx") +//FX_IDENTIFIER(SwapFieldsFx, "swapFieldsFx") +//FX_IDENTIFIER(DeInterlaceFx, "deInterlaceFx") +//FX_IDENTIFIER(InterlaceFx, "interlaceFx") diff --git a/toonz/sources/common/tfx/zeraryFx.cpp b/toonz/sources/common/tfx/zeraryFx.cpp new file mode 100644 index 0000000..3ac4f0c --- /dev/null +++ b/toonz/sources/common/tfx/zeraryFx.cpp @@ -0,0 +1,114 @@ + + +// TnzCore includes +#include "trop.h" +#include "tpixelutils.h" + +// TnzBase includes +#include "tparamset.h" +#include "tdoubleparam.h" +#include "tzeraryfx.h" +#include "tfxparam.h" +#include "tparamuiconcept.h" + +#include "tbasefx.h" +#include "tzeraryfx.h" + +//================================================================== + +class ColorCardFx : public TBaseZeraryFx +{ + FX_DECLARATION(ColorCardFx) + + TPixelParamP m_color; + +public: + ColorCardFx() : m_color(TPixel32::Green) + { + bindParam(this, "color", m_color); + m_color->setDefaultValue(TPixel32::Green); + setName(L"ColorCardFx"); + } + + bool canHandle(const TRenderSettings &info, double frame) { return true; } + + bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) + { + bBox = TConsts::infiniteRectD; + return true; + } + + void doCompute(TTile &tile, double frame, const TRenderSettings &) + { + TRaster32P raster32 = tile.getRaster(); + if (raster32) + raster32->fill(m_color->getPremultipliedValue(frame)); + else { + TRaster64P ras64 = tile.getRaster(); + if (ras64) + ras64->fill(toPixel64(m_color->getPremultipliedValue(frame))); + else + throw TException("ColorCardFx unsupported pixel type"); + } + }; +}; + +//================================================================== + +class CheckBoardFx : public TBaseZeraryFx +{ + FX_DECLARATION(CheckBoardFx) + + TPixelParamP m_color1, m_color2; + TDoubleParamP m_size; + +public: + CheckBoardFx() : m_color1(TPixel32::Black), m_color2(TPixel32::White), m_size(50) + { + m_size->setMeasureName("fxLength"); + bindParam(this, "color1", m_color1); + bindParam(this, "color2", m_color2); + bindParam(this, "size", m_size); + m_color1->setDefaultValue(TPixel32::Black); + m_color2->setDefaultValue(TPixel32::White); + m_size->setValueRange(1, 1000); + m_size->setDefaultValue(50); + setName(L"CheckBoardFx"); + } + + bool canHandle(const TRenderSettings &info, double frame) { return false; } + + bool doGetBBox(double, TRectD &bBox, const TRenderSettings &info) + { + bBox = TConsts::infiniteRectD; + return true; + } + + void doCompute(TTile &tile, double frame, const TRenderSettings &info) + { + const TPixel32 &c1 = m_color1->getValue(frame); + const TPixel32 &c2 = m_color2->getValue(frame); + + double size = m_size->getValue(frame); + + assert(info.m_shrinkX == info.m_shrinkY); + size *= info.m_affine.a11 / info.m_shrinkX; + + TDimensionD dim(size, size); + TRop::checkBoard(tile.getRaster(), c1, c2, dim, tile.m_pos); + } + + void getParamUIs(TParamUIConcept *&concepts, int &length) + { + concepts = new TParamUIConcept[length = 1]; + + concepts[0].m_type = TParamUIConcept::SIZE; + concepts[0].m_label = "Size"; + concepts[0].m_params.push_back(m_size); + } +}; + +//================================================================== + +FX_IDENTIFIER(ColorCardFx, "colorCardFx") +FX_IDENTIFIER(CheckBoardFx, "checkBoardFx") diff --git a/toonz/sources/common/tgeometry/tcurves.cpp b/toonz/sources/common/tgeometry/tcurves.cpp new file mode 100644 index 0000000..a9ba86a --- /dev/null +++ b/toonz/sources/common/tgeometry/tcurves.cpp @@ -0,0 +1,676 @@ + + +#include "tmachine.h" +#include "tcurves.h" +#include "tcurveutil.h" +#include "tmathutil.h" +#include "tbezier.h" + +using namespace std; + +//============================================================================= + +ostream &operator<<(ostream &out, const TSegment &segment) +{ + return out << "S{" << segment.getP0() << ", " << segment.getP1() << "}"; +} + +//============================================================================= + +void TCubic::split(double t, TCubic &first, TCubic &second) const +{ + double s = 1.0 - t; + + TPointD H = s * m_p1 + t * m_p2; + + first.m_p0 = m_p0; + first.m_p1 = s * m_p0 + t * m_p1; + first.m_p2 = s * first.m_p1 + t * H; + + second.m_p3 = m_p3; + second.m_p2 = s * m_p2 + t * m_p3; + second.m_p1 = s * H + t * second.m_p2; + + first.m_p3 = s * first.m_p2 + t * second.m_p1; + second.m_p0 = first.m_p3; +} + +double TCubic::getLength(double t0, double t1) const +{ + return -1; +} + +//============================================================================= + +TPointD TQuadratic::getPoint(double t) const +{ + double s = 1 - t; + return m_p0 * s * s + 2 * t * s * m_p1 + t * t * m_p2; +} + +//----------------------------------------------------------------------------- + +double TQuadratic::getX(double t) const +{ + double s = 1 - t; + return m_p0.x * s * s + 2 * t * s * m_p1.x + t * t * m_p2.x; +} +//----------------------------------------------------------------------------- + +double TQuadratic::getY(double t) const +{ + double s = 1 - t; + return m_p0.y * s * s + 2 * t * s * m_p1.y + t * t * m_p2.y; +} +//----------------------------------------------------------------------------- + +double TQuadratic::getT(const TPointD &p) const +{ + + // risolvo l'equazione min|| b(t) - p || + + // esprimo b in forma di polinomio ed ottengo + // + // || 2 || + //min || a t + b t + c - p || + // || || + // + // il tutto si riconduce a cercare le radici + // di un'equazione del tipo + // 2 3 2 2 + // 2·a ·t + 3·a·b·t + t·(2·a·v + b ) + b·v + // dove v e' pari a c - p + + vector + bez(3), + poly(3); + + bez[0] = m_p0; + bez[1] = m_p1; + bez[2] = m_p2; + + bezier2poly(bez, poly); + + TPointD v = poly[0] - p; + + vector toSolve(4); + vector sol; + + toSolve[3] = 2.0 * norm2(poly[2]); + toSolve[2] = 3.0 * (poly[2].x * poly[1].x + poly[2].y * poly[1].y); + toSolve[1] = 2.0 * (poly[2].x * v.x + poly[2].y * v.y) + norm2(poly[1]); + toSolve[0] = (poly[1].x * v.x + poly[1].y * v.y); + + int nSol = rootFinding(toSolve, sol); + + if (-1 == nSol) // infinite soluzioni + return 0; + + int minParameter = -1; + double minDist = (std::numeric_limits::max)(); + + for (int i = 0; i < nSol; ++i) { + if (sol[i] < 0.0) + sol[i] = 0.0; + else if (sol[i] > 1.0) + sol[i] = 1.0; + + double tmpDist = tdistance2(p, getPoint(sol[i])); + if (tmpDist < minDist) { + minDist = tmpDist; + minParameter = i; + } + } + + if (minParameter != -1) + return sol[minParameter]; + + return tdistance2(m_p0, p) < tdistance2(m_p2, p) ? 0 : 1; +} + +//----------------------------------------------------------------------------- + +void TQuadratic::split(double t, TQuadratic &left, TQuadratic &right) const +{ + double dt; + TPointD p; + dt = 1.0 - t; + + left.m_p0 = m_p0; + right.m_p2 = m_p2; + + left.m_p1 = dt * m_p0 + t * m_p1; + right.m_p1 = dt * m_p1 + t * m_p2; + p = dt * left.m_p1 + t * right.m_p1; + + left.m_p2 = right.m_p0 = p; +} + +//----------------------------------------------------------------------------- + +TRectD TQuadratic::getBBox() const +{ + TRectD bBox; + if (m_p0.x < m_p2.x) + bBox.x0 = m_p0.x, bBox.x1 = m_p2.x; + else + bBox.x0 = m_p2.x, bBox.x1 = m_p0.x; + + if (m_p0.y < m_p2.y) + bBox.y0 = m_p0.y, bBox.y1 = m_p2.y; + else + bBox.y0 = m_p2.y, bBox.y1 = m_p0.y; + + TPointD denom = 2 * m_p1 - m_p0 - m_p2; + if (denom.x != 0) { + double tx = (m_p1.x - m_p0.x) / denom.x; + if (tx >= 0 && tx <= 1) { + double x = getPoint(tx).x; + if (x < bBox.x0) + bBox.x0 = x; + else if (x > bBox.x1) + bBox.x1 = x; + } + } + if (denom.y != 0) { + double ty = (m_p1.y - m_p0.y) / denom.y; + if (ty >= 0 && ty <= 1) { + double y = getPoint(ty).y; + if (y < bBox.y0) + bBox.y0 = y; + else if (y > bBox.y1) + bBox.y1 = y; + } + } + + return bBox; +} + +/*! +Calcolo della curvatura per una Quadratica. +Vedi Farin pag.176 per la spiegazione della formula +usata. +*/ +double TQuadratic::getCurvature(double t) const +{ + assert(0 <= t && t <= 1.0); + + TQuadratic q1, q2; + + split(t, q1, q2); + + double signum = 1.0; + if (areAlmostEqual(t, 1.0)) { + signum *= -1.0; + std::swap(q1, q2); + std::swap(q2.m_p0, q2.m_p2); + } + + TPointD v_1_0(q2.m_p1 - q2.m_p0); + + double + a = norm2(v_1_0); + + if (isAlmostZero(a)) + return (std::numeric_limits::max)(); + + a = 1.0 / sqrt(a); + + double + b = cross(v_1_0 * a, q2.m_p2 - q2.m_p0); + + return 0.5 * signum * b / a; +} + +//----------------------------------------------------------------------------- + +double TQuadratic::getLength(double t0, double t1) const +{ + TQuadraticLengthEvaluator lengthEval(*this); + + t0 = min(max(0.0, t0), 1.0); // backward compatibility + t1 = min(max(0.0, t1), 1.0); // backward compatibility + if (t0 > t1) + std::swap(t0, t1); + + if (t0 > 0.0) + return lengthEval.getLengthAt(t1) - lengthEval.getLengthAt(t0); + + return lengthEval.getLengthAt(t1); +} + +double TQuadratic::getApproximateLength(double t0, double t1, double error) const +{ + if (t0 == t1) + return 0; + + t0 = min(max(0.0, t0), 1.0); + t1 = min(max(0.0, t1), 1.0); + + if (t0 > t1) + std::swap(t0, t1); + + TQuadratic q; + + if (t0 == 0.0 && t1 == 1.0) + q = *this; + else { + TQuadratic q1; + split(t0, q, q1); + + assert(t0 != 1.0); + + double newPar = (t1 - t0) / (1.0 - t0); + q1.split(newPar, q, q1); + } + + double step = computeStep(q, error); + + double length = 0.0; + + TPointD p1 = q.getP0(); + TPointD p2; + for (double t = step; t < 1.0; t += step) { + p2 = q.getPoint(t); + length += tdistance(p1, p2); + p1 = p2; + } + length += tdistance(p1, q.getP2()); + + return length; +} + +//----------------------------------------------------------------------------- + +int TQuadratic::getX(double y, double &x0, double &x1) const +{ + int ret = 0; + double t; + + if (y > getBBox().y1 || y < getBBox().y0) + return 0; + + double a = getP0().y - 2 * getP1().y + getP2().y; + double half_b = getP1().y - getP0().y; + double c = getP0().y - y; + + if (a == 0) //segment + { + if (half_b == 0) //horizontal segment, or point + { + if (c == 0) { + x0 = getP0().x; + x1 = getP2().x; + return 2; + } else + return 0; + } else { + t = -c / (2 * half_b); + if (t >= 0 && t <= 1) { + x0 = getPoint(t).x; + return 1; + } + } + } + + double discr = half_b * half_b - a * c; + + if (discr < 0) + return 0; + + double coeff = 1.0 / a; + double coeff1 = -half_b * coeff; + + if (discr == 0) { + t = coeff1; + if (t >= 0 && t <= 1) { + ret = 2; + x0 = x1 = getPoint(t).x; + } + } else { + discr = sqrt(discr) * coeff; + t = coeff1 + discr; + if (t >= 0 && t <= 1) { + ret++; + x0 = getPoint(t).x; + } + + t = coeff1 - discr; + if (t >= 0 && t <= 1) { + ret++; + if (ret == 2) + x1 = getPoint(t).x; + else + x0 = getPoint(t).x; + } + } + return ret; +} + +int TQuadratic::getY(double y, double &y0, double &y1) const +{ + TQuadratic temp(*this); + + swap(temp.m_p0.x, temp.m_p0.y); + swap(temp.m_p1.x, temp.m_p1.y); + swap(temp.m_p2.x, temp.m_p2.y); + + return temp.getX(y, y0, y1); +} +//============================================================================= +TPointD TCubic::getPoint(double t) const +{ + double s = 1 - t; + return m_p0 * s * s * s + 3 * t * s * (s * m_p1 + t * m_p2) + t * t * t * m_p3; +} +//----------------------------------------------------------------------------- +TPointD TCubic::getSpeed(double t) const +{ + double s = 1 - t; + return 3.0 * ((m_p1 - m_p0) * s * s + 2 * (m_p2 - m_p0) * s * t + (m_p3 - m_p2) * t * t); +} +//============================================================================= + +TThickQuadratic::TThickQuadratic() + : TQuadratic(), m_thickP0(0), m_thickP1(0), m_thickP2(0) +{ +} + +//----------------------------------------------------------------------------- + +TThickQuadratic::TThickQuadratic(const TQuadratic &q) + : TQuadratic(q), m_thickP0(0.0), m_thickP1(0.0), m_thickP2(0.0) +{ +} + +//----------------------------------------------------------------------------- + +TThickQuadratic::TThickQuadratic(const TPointD &p0, double thickP0, + const TPointD &p1, double thickP1, + const TPointD &p2, double thickP2) + : TQuadratic(p0, p1, p2), m_thickP0(thickP0), m_thickP1(thickP1), m_thickP2(thickP2) +{ +} + +//----------------------------------------------------------------------------- + +TThickQuadratic::TThickQuadratic(const TThickPoint &p0, + const TThickPoint &p1, + const TThickPoint &p2) + : TQuadratic(TPointD(p0.x, p0.y), TPointD(p1.x, p1.y), TPointD(p2.x, p2.y)), m_thickP0(p0.thick), m_thickP1(p1.thick), m_thickP2(p2.thick) +{ +} + +//----------------------------------------------------------------------------- + +TThickQuadratic::TThickQuadratic(const TThickQuadratic &thickQuadratic) + : TQuadratic(thickQuadratic), m_thickP0(thickQuadratic.m_thickP0), m_thickP1(thickQuadratic.m_thickP1), m_thickP2(thickQuadratic.m_thickP2) +{ +} + +//----------------------------------------------------------------------------- +void TThickQuadratic::setThickP0(const TThickPoint &p) +{ + m_p0 = p; + m_thickP0 = p.thick; +} + +//----------------------------------------------------------------------------- +void TThickQuadratic::setThickP1(const TThickPoint &p) +{ + m_p1 = p; + m_thickP1 = p.thick; +} + +//----------------------------------------------------------------------------- +void TThickQuadratic::setThickP2(const TThickPoint &p) +{ + m_p2 = p; + m_thickP2 = p.thick; +} + +//----------------------------------------------------------------------------- +TThickPoint TThickQuadratic::getThickPoint(double t) const +{ + double s = 1 - t; + return TThickPoint(m_p0 * s * s + 2 * t * s * m_p1 + t * t * m_p2, m_thickP0 * s * s + 2 * t * s * m_thickP1 + t * t * m_thickP2); +} + +//----------------------------------------------------------------------------- +void TThickQuadratic::split(double t, TThickQuadratic &left, TThickQuadratic &right) const +{ + double dt; + TPointD p; + dt = 1.0 - t; + + // control points + left.m_p0 = m_p0; + right.m_p2 = m_p2; + + left.m_p1 = dt * m_p0 + t * m_p1; + right.m_p1 = dt * m_p1 + t * m_p2; + p = dt * left.m_p1 + t * right.m_p1; + + left.m_p2 = right.m_p0 = p; + + // thick points + left.m_thickP0 = m_thickP0; + right.m_thickP2 = m_thickP2; + + left.m_thickP1 = dt * m_thickP0 + t * m_thickP1; + right.m_thickP1 = dt * m_thickP1 + t * m_thickP2; + + // store thickness of intermediary point + p.x = dt * left.m_thickP1 + t * right.m_thickP1; + + left.m_thickP2 = right.m_thickP0 = p.x; +} + +//----------------------------------------------------------------------------- + +TRectD TThickQuadratic::getBBox() const +{ + + TRectD bBox = TQuadratic::getBBox(); + + double maxRadius = tmax(m_thickP0, m_thickP1, m_thickP2); + if (maxRadius > 0) { + // bBox.enlarge(maxRadius) si comporta male nel caso bBox.isEmpty() + bBox.x0 -= maxRadius; + bBox.y0 -= maxRadius; + + bBox.x1 += maxRadius; + bBox.y1 += maxRadius; + } + + return bBox; +} + +// ============================================================================ +// Methods of the class TThickCubic +// ============================================================================ + +TThickCubic::TThickCubic() + : TCubic(), m_thickP0(0), m_thickP1(0), m_thickP2(0), m_thickP3(0) +{ +} + +//----------------------------------------------------------------------------- + +TThickCubic::TThickCubic(const TPointD &p0, double thickP0, + const TPointD &p1, double thickP1, + const TPointD &p2, double thickP2, + const TPointD &p3, double thickP3) + : TCubic(p0, p1, p2, p3), m_thickP0(thickP0), m_thickP1(thickP1), m_thickP2(thickP2), m_thickP3(thickP3) +{ +} + +//----------------------------------------------------------------------------- + +TThickCubic::TThickCubic(const TThickPoint &p0, + const TThickPoint &p1, + const TThickPoint &p2, + const TThickPoint &p3) + : TCubic(TPointD(p0.x, p0.y), TPointD(p1.x, p1.y), TPointD(p2.x, p2.y), TPointD(p3.x, p3.y)), m_thickP0(p0.thick), m_thickP1(p1.thick), m_thickP2(p2.thick), m_thickP3(p3.thick) +{ +} +// tonino *************************************************************** + +TThickCubic::TThickCubic(const T3DPointD &p0, + const T3DPointD &p1, + const T3DPointD &p2, + const T3DPointD &p3) + : TCubic(TPointD(p0.x, p0.y), TPointD(p1.x, p1.y), TPointD(p2.x, p2.y), TPointD(p3.x, p3.y)), m_thickP0(p0.z), m_thickP1(p1.z), m_thickP2(p2.z), m_thickP3(p3.z) +{ +} + +// tonino *************************************************************** + +//----------------------------------------------------------------------------- + +TThickCubic::TThickCubic(const TThickCubic &thickCubic) + : TCubic(thickCubic), m_thickP0(thickCubic.m_thickP0), m_thickP1(thickCubic.m_thickP1), m_thickP2(thickCubic.m_thickP2), m_thickP3(thickCubic.m_thickP3) +{ +} + +//----------------------------------------------------------------------------- + +void TThickCubic::setThickP0(const TThickPoint &p) +{ + m_p0.x = p.x; + m_p0.y = p.y; + m_thickP0 = p.thick; +} + +//----------------------------------------------------------------------------- + +void TThickCubic::setThickP1(const TThickPoint &p) +{ + m_p1.x = p.x; + m_p1.y = p.y; + m_thickP1 = p.thick; +} + +//----------------------------------------------------------------------------- + +void TThickCubic::setThickP2(const TThickPoint &p) +{ + m_p2.x = p.x; + m_p2.y = p.y; + m_thickP2 = p.thick; +} + +//----------------------------------------------------------------------------- +void TThickCubic::setThickP3(const TThickPoint &p) +{ + m_p3.x = p.x; + m_p3.y = p.y; + m_thickP3 = p.thick; +} + +//----------------------------------------------------------------------------- + +TThickPoint TThickCubic::getThickPoint(double t) const +{ + double + thick_l1, + thick_h, + thick_r3; + + double s = 1.0 - t; + + TPointD l1(m_p0 * s + m_p1 * t); + thick_l1 = m_thickP0 * s + m_thickP1 * t; + + TPointD h(m_p1 * s + m_p2 * t); + thick_h = m_thickP1 * s + m_thickP2 * t; + + TPointD r3(m_p2 * s + m_p3 * t); + thick_r3 = m_thickP2 * s + m_thickP3 * t; + + // adesso riutilizzo le variabili gia' utilizzate + + // l2 + l1 = l1 * s + h * t; + thick_l1 = thick_l1 * s + thick_h * t; + + // r1 + r3 = h * s + r3 * t; + thick_r3 = thick_h * s + thick_r3 * t; + + // l3-r0 + h = l1 * s + r3 * t; + thick_h = thick_l1 * s + thick_r3 * t; + + return TThickPoint(h, thick_h); +} + +//----------------------------------------------------------------------------- + +void TThickCubic::split(double t, TThickCubic &first, TThickCubic &second) const +{ + double s = 1.0 - t; + + TPointD H(m_p1 * s + m_p2 * t); + double thick_h = m_thickP1 * s + m_thickP2 * t; + + first.m_p0 = m_p0; + first.m_thickP0 = m_thickP0; + + first.m_p1 = m_p0 * s + m_p1 * t; + first.m_thickP1 = m_thickP0 * s + m_thickP1 * t; + + first.m_p2 = first.m_p1 * s + H * t; + first.m_thickP2 = first.m_thickP1 * s + thick_h * t; + + second.m_p3 = m_p3; + second.m_thickP3 = m_thickP3; + + second.m_p2 = m_p2 * s + m_p3 * t; + second.m_thickP2 = m_thickP2 * s + m_thickP3 * t; + + second.m_p1 = H * s + second.m_p2 * t; + second.m_thickP1 = thick_h * s + second.m_thickP2 * t; + + first.m_p3 = first.m_p2 * s + second.m_p1 * t; + first.m_thickP3 = first.m_thickP2 * s + second.m_thickP1 * t; + + second.m_p0 = first.m_p3; + second.m_thickP0 = first.m_thickP3; +} + +//----------------------------------------------------------------------------- + +ostream &operator<<(ostream &out, const TQuadratic &curve) +{ + return out << "Q{" << curve.getP0() << ", " << curve.getP1() << ", " + << curve.getP2() << "}"; +} + +ostream &operator<<(ostream &out, const TCubic &curve) +{ + return out << "C{" << curve.getP0() << ", " << curve.getP1() << ", " + << curve.getP2() << ", " << curve.getP3() << "}"; +} + +ostream &operator<<(ostream &out, const TThickSegment &segment) +{ + return out << "TS{" << segment.getThickP0() << ", " << segment.getThickP1() << "}"; +} + +ostream &operator<<(ostream &out, const TThickQuadratic &tq) +{ + return out << "TQ{" << tq.getThickP0() << ", " << tq.getThickP1() << ", " << tq.getThickP2() << "}"; +} + +ostream &operator<<(ostream &out, const TThickCubic &tc) +{ + return out << "TC{" << tc.getThickP0() << ", " + << tc.getThickP1() << ", " + << tc.getThickP2() << ", " + << tc.getThickP3() << "}"; +} + +//----------------------------------------------------------------------------- +// End Of File +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tgeometry/tcurveutil.cpp b/toonz/sources/common/tgeometry/tcurveutil.cpp new file mode 100644 index 0000000..3ba356b --- /dev/null +++ b/toonz/sources/common/tgeometry/tcurveutil.cpp @@ -0,0 +1,626 @@ + + +#include "tcurveutil.h" +#include "tcurves.h" +#include "tmathutil.h" +#include "tbezier.h" + +//============================================================================= + +/* +Questa funzione ritorna un vettore di +coppie di double (DoublePair) che individua i parametri +dei punti d'intersezione. + + L'intero restituito indica il numero d'intersezioni che + sono state individuate (per due segmenti una). + + Se i segmenti sono paralleli il parametro viene posto a -1. +*/ + +int intersect(const TSegment &first, + const TSegment &second, + std::vector &intersections) +{ + return intersect(first.getP0(), first.getP1(), second.getP0(), second.getP1(), intersections); +} + +int intersect(const TPointD &p1, const TPointD &p2, const TPointD &p3, const TPointD &p4, + std::vector &intersections) +{ + // This algorithm is presented in Graphics Geems III pag 199 + + static double Ax, Bx, Ay, By, Cx, Cy, d, f, e; + static double x1lo, x1hi, y1lo, y1hi; + + Ax = p2.x - p1.x; + Bx = p3.x - p4.x; + + //test delle BBox + if (Ax < 0.0) { + x1lo = p2.x; + x1hi = p1.x; + } else { + x1lo = p1.x; + x1hi = p2.x; + } + + if (Bx > 0.0) { + if (x1hi < p4.x || x1lo > p3.x) + return 0; + } else if (x1hi < p3.x || x1lo > p4.x) + return 0; + + Ay = p2.y - p1.y; + By = p3.y - p4.y; + + if (Ay < 0) { + y1lo = p2.y; + y1hi = p1.y; + } else { + y1lo = p1.y; + y1hi = p2.y; + } + + if (By > 0) { + if (y1hi < p4.y || y1lo > p3.y) + return 0; + } else if (y1hi < p3.y || y1lo > p4.y) + return 0; + + Cx = p1.x - p3.x; + Cy = p1.y - p3.y; + + d = By * Cx - Bx * Cy; + f = Ay * Bx - Ax * By; + e = Ax * Cy - Ay * Cx; + + if (f > 0) { + if (d < 0) + return 0; + + if (!areAlmostEqual(d, f)) + if (d > f) + return 0; + + if (e < 0) + return 0; + if (!areAlmostEqual(e, f)) + if (e > f) + return 0; + } else if (f < 0) { + if (d > 0) + return 0; + + if (!areAlmostEqual(d, f)) + if (d < f) + return 0; + + if (e > 0) + return 0; + if (!areAlmostEqual(e, f)) + if (e < f) + return 0; + } else { + if (d < 0 || d > 1 || e < 0 || e > 1) + return 0; + + if (p1 == p2 && p3 == p4) { + intersections.push_back(DoublePair(0, 0)); + return 1; + } + + // controllo che i segmenti non siano sulla stessa retta + if (!cross(p2 - p1, p4 - p1)) { + // calcolo delle combinazioni baricentriche + double distp2p1 = norm2(p2 - p1); + double distp3p4 = norm2(p3 - p4); + + double dist2_p3p1 = norm2(p3 - p1); + double dist2_p4p1 = norm2(p4 - p1); + double dist2_p3p2 = norm2(p3 - p2); + double dist2_p4p2 = norm2(p4 - p2); + + int intersection = 0; + + // calcolo delle prime due soluzioni + double vol1; + + if (distp3p4) { + distp3p4 = sqrt(distp3p4); + + vol1 = (p1 - p3) * normalize(p4 - p3); + + if (vol1 >= 0 && vol1 <= distp3p4) // combinazione baricentrica valida + { + intersections.push_back(DoublePair(0.0, vol1 / distp3p4)); + ++intersection; + } + + vol1 = (p2 - p3) * normalize(p4 - p3); + + if (vol1 >= 0 && vol1 <= distp3p4) { + intersections.push_back(DoublePair(1.0, vol1 / distp3p4)); + ++intersection; + } + } + + if (distp2p1) { + distp2p1 = sqrt(distp2p1); + + vol1 = (p3 - p1) * normalize(p2 - p1); + + if (dist2_p3p2 && dist2_p3p1) + if (vol1 >= 0 && vol1 <= distp2p1) { + intersections.push_back(DoublePair(vol1 / distp2p1, 0.0)); + ++intersection; + } + + vol1 = (p4 - p1) * normalize(p2 - p1); + + if (dist2_p4p2 && dist2_p4p1) + if (vol1 >= 0 && vol1 <= distp2p1) { + intersections.push_back(DoublePair(vol1 / distp2p1, 1.0)); + ++intersection; + } + } + return intersection; + } + return -1; + } + + double par_s = d / f; + double par_t = e / f; + + intersections.push_back(DoublePair(par_s, par_t)); + return 1; +} + +//------------------------------------------------------------------------------------------------------------ +int intersectCloseControlPoints(const TQuadratic &c0, + const TQuadratic &c1, + std::vector &intersections); + +int intersect(const TQuadratic &c0, + const TQuadratic &c1, + std::vector &intersections, bool checksegments) +{ + int ret; + + // funziona male, a volte toppa le intersezioni... + if (checksegments) { + ret = intersectCloseControlPoints(c0, c1, intersections); + if (ret != -2) + return ret; + } + + double a = c0.getP0().x - 2 * c0.getP1().x + c0.getP2().x; + double b = 2 * (c0.getP1().x - c0.getP0().x); + double d = c0.getP0().y - 2 * c0.getP1().y + c0.getP2().y; + double e = 2 * (c0.getP1().y - c0.getP0().y); + + double coeff = b * d - a * e; + int i = 0; + + if (areAlmostEqual(coeff, 0.0)) //c0 is a Segment, or a single point!!! + { + + TSegment s = TSegment(c0.getP0(), c0.getP2()); + ret = intersect(s, c1, intersections); + if (a == 0 && d == 0) //values of t in s coincide with values of t in c0 + return ret; + + for (i = intersections.size() - ret; i < (int)intersections.size(); i++) { + intersections[i].first = c0.getT(s.getPoint(intersections[i].first)); + } + return ret; + } + + double c = c0.getP0().x; + double f = c0.getP0().y; + + double g = c1.getP0().x - 2 * c1.getP1().x + c1.getP2().x; + double h = 2 * (c1.getP1().x - c1.getP0().x); + double k = c1.getP0().x; + + double m = c1.getP0().y - 2 * c1.getP1().y + c1.getP2().y; + double p = 2 * (c1.getP1().y - c1.getP0().y); + double q = c1.getP0().y; + + if (areAlmostEqual(h * m - g * p, 0.0)) //c1 is a Segment, or a single point!!! + { + TSegment s = TSegment(c1.getP0(), c1.getP2()); + ret = intersect(c0, s, intersections); + if (g == 0 && m == 0) //values of t in s coincide with values of t in c0 + return ret; + + for (i = intersections.size() - ret; i < (int)intersections.size(); i++) { + intersections[i].second = c1.getT(s.getPoint(intersections[i].second)); + } + return ret; + } + + double a2 = (g * d - a * m); + double b2 = (h * d - a * p); + double c2 = ((k - c) * d + (f - q) * a); + + coeff = 1.0 / coeff; + + double A = (a * a + d * d) * coeff * coeff; + double aux = A * c2 + (a * b + d * e) * coeff; + + vector t; + vector solutions; + + t.push_back(aux * c2 + a * c + d * f - k * a - d * q); + aux += A * c2; + t.push_back(aux * b2 - h * a - d * p); + t.push_back(aux * a2 + A * b2 * b2 - g * a - d * m); + t.push_back(2 * A * a2 * b2); + t.push_back(A * a2 * a2); + + rootFinding(t, solutions); + // solutions.push_back(0.0); //per convenzione; un valore vale l'altro.... + + for (i = 0; i < (int)solutions.size(); i++) { + if (solutions[i] < 0) { + if (areAlmostEqual(solutions[i], 0, 1e-6)) + solutions[i] = 0; + else + continue; + } else if (solutions[i] > 1) { + if (areAlmostEqual(solutions[i], 1, 1e-6)) + solutions[i] = 1; + else + continue; + } + + DoublePair tt; + tt.second = solutions[i]; + tt.first = coeff * (tt.second * (a2 * tt.second + b2) + c2); + if (tt.first < 0) { + if (areAlmostEqual(tt.first, 0, 1e-6)) + tt.first = 0; + else + continue; + } else if (tt.first > 1) { + if (areAlmostEqual(tt.first, 1, 1e-6)) + tt.first = 1; + else + continue; + } + + intersections.push_back(tt); + + assert(areAlmostEqual(c0.getPoint(tt.first), c1.getPoint(tt.second), 1e-1)); + } + return intersections.size(); +} + +//============================================================================= +//questa funzione verifica se il punto di controllo p1 e' molto vicino a p0 o a p2: +//in tal caso, si approssima la quadratica al segmento p0-p2. +//se p1 e' vicino a p0, la relazione che lega il t del segmento al t della quadratica originaria e' tq = sqrt(ts), +//se p1 e' vicino a p2, invece e' tq = 1-sqrt(1-ts). + +int intersectCloseControlPoints(const TQuadratic &c0, + const TQuadratic &c1, + std::vector &intersections) +{ + int ret = -2; + + double dist1 = tdistance2(c0.getP0(), c0.getP1()); + if (dist1 == 0) + dist1 = 1e-20; + double dist2 = tdistance2(c0.getP1(), c0.getP2()); + if (dist2 == 0) + dist2 = 1e-20; + double val0 = tmax(dist1, dist2) / tmin(dist1, dist2); + double dist3 = tdistance2(c1.getP0(), c1.getP1()); + if (dist3 == 0) + dist3 = 1e-20; + double dist4 = tdistance2(c1.getP1(), c1.getP2()); + if (dist4 == 0) + dist4 = 1e-20; + double val1 = tmax(dist3, dist4) / tmin(dist3, dist4); + + if (val0 > 1000000 && val1 > 1000000) //entrambe c0 e c1 approssimate a segmenti + { + TSegment s0 = TSegment(c0.getP0(), c0.getP2()); + TSegment s1 = TSegment(c1.getP0(), c1.getP2()); + ret = intersect(s0, s1, intersections); + for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) { + intersections[i].first = (dist1 < dist2) ? sqrt(intersections[i].first) : 1 - sqrt(1 - intersections[i].first); + intersections[i].second = (dist3 < dist4) ? sqrt(intersections[i].second) : 1 - sqrt(1 - intersections[i].second); + } + //return ret; + } else if (val0 > 1000000) //solo c0 approssimata a segmento + { + TSegment s0 = TSegment(c0.getP0(), c0.getP2()); + ret = intersect(s0, c1, intersections); + for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) + intersections[i].first = (dist1 < dist2) ? sqrt(intersections[i].first) : 1 - sqrt(1 - intersections[i].first); + //return ret; + } else if (val1 > 1000000) //solo c1 approssimata a segmento + { + TSegment s1 = TSegment(c1.getP0(), c1.getP2()); + ret = intersect(c0, s1, intersections); + for (UINT i = intersections.size() - ret; i < (int)intersections.size(); i++) + intersections[i].second = (dist3 < dist4) ? sqrt(intersections[i].second) : 1 - sqrt(1 - intersections[i].second); + //return ret; + } + + /* +if (ret!=-2) + { + std::vector intersections1; + int ret1 = intersect(c0, c1, intersections1, false); + if (ret1>ret) + { + intersections = intersections1; + return ret1; + } + } +*/ + + return ret; +} + +//============================================================================= + +int intersect(const TQuadratic &q, + const TSegment &s, + std::vector &intersections, + bool firstIsQuad) +{ + int solutionNumber = 0; + + // nota la retta a*x+b*y+c = 0 andiamo alla ricerca delle soluzioni + // di a*x(t)+b*y(t)+c=0 in [0,1] + double + a = s.getP0().y - s.getP1().y, + b = s.getP1().x - s.getP0().x, + c = -(a * s.getP0().x + b * s.getP0().y); + + // se il segmento e' un punto + if (0.0 == a && 0.0 == b) { + double outParForQuad = q.getT(s.getP0()); + + if (areAlmostEqual(q.getPoint(outParForQuad), s.getP0())) { + if (firstIsQuad) + intersections.push_back(DoublePair(outParForQuad, 0)); + else + intersections.push_back(DoublePair(0, outParForQuad)); + return 1; + } + return 0; + } + + if (q.getP2() - q.getP1() == q.getP1() - q.getP0()) //pure il secondo e' unsegmento.... + if (firstIsQuad) + return intersect(TSegment(q.getP0(), q.getP2()), s, intersections); + else + return intersect(s, TSegment(q.getP0(), q.getP2()), intersections); + + vector bez, pol; + bez.push_back(q.getP0()); + bez.push_back(q.getP1()); + bez.push_back(q.getP2()); + + bezier2poly(bez, pol); + + vector poly_1(3, 0), sol; + + poly_1[0] = a * pol[0].x + b * pol[0].y + c; + poly_1[1] = a * pol[1].x + b * pol[1].y; + poly_1[2] = a * pol[2].x + b * pol[2].y; + + if (!(rootFinding(poly_1, sol))) + return 0; + + double + segmentPar, + solution; + + TPointD v10(s.getP1() - s.getP0()); + for (UINT i = 0; i < sol.size(); ++i) { + solution = sol[i]; + if ((0.0 <= solution && solution <= 1.0) || + areAlmostEqual(solution, 0.0, 1e-6) || + areAlmostEqual(solution, 1.0, 1e-6)) { + segmentPar = (q.getPoint(solution) - s.getP0()) * v10 / (v10 * v10); + if ((0.0 <= segmentPar && segmentPar <= 1.0) || + areAlmostEqual(segmentPar, 0.0, 1e-6) || + areAlmostEqual(segmentPar, 1.0, 1e-6)) { + TPointD p1 = q.getPoint(solution); + TPointD p2 = s.getPoint(segmentPar); + assert(areAlmostEqual(p1, p2, 1e-1)); + + if (firstIsQuad) + intersections.push_back(DoublePair(solution, segmentPar)); + else + intersections.push_back(DoublePair(segmentPar, solution)); + solutionNumber++; + } + } + } + + return solutionNumber; +} + +//============================================================================= + +bool isCloseToSegment(const TPointD &point, const TSegment &segment, double distance) +{ + TPointD a = segment.getP0(); + TPointD b = segment.getP1(); + double lenght2 = tdistance2(a, b); + if (lenght2 < tdistance2(a, point) || lenght2 < tdistance2(point, b)) + return false; + if (a.x == b.x) + return fabs(point.x - a.x) <= distance; + if (a.y == b.y) + return fabs(point.y - a.y) <= distance; + + // y=mx+q + double m = (a.y - b.y) / (a.x - b.x); + double q = a.y - (m * a.x); + + double d2 = pow(fabs(point.y - (m * point.x) - q), 2) / (1 + (m * m)); + return d2 <= distance * distance; +} + +//============================================================================= + +double tdistance(const TSegment &segment, const TPointD &point) +{ + TPointD v1 = segment.getP1() - segment.getP0(); + TPointD v2 = point - segment.getP0(); + TPointD v3 = point - segment.getP1(); + + if (v2 * v1 <= 0) + return tdistance(point, segment.getP0()); + else if (v3 * v1 >= 0) + return tdistance(point, segment.getP1()); + + return fabs(v2 * rotate90(normalize(v1))); +} + +//----------------------------------------------------------------------------- +/* +This formule is derived from Graphic Gems pag. 600 + + e = h^2 |a|/8 + + e = pixel size + h = step + a = acceleration of curve (for a quadratic is a costant value) +*/ + +double computeStep(const TQuadratic &quad, double pixelSize) +{ + double step = 2; + + TPointD A = quad.getP0() - 2.0 * quad.getP1() + quad.getP2(); // 2*A is the acceleration of the curve + + double A_len = norm(A); + + /* + A_len is equal to 2*norm(a) + pixelSize will be 0.5*pixelSize + now h is equal to sqrt( 8 * 0.5 * pixelSize / (2*norm(a)) ) = sqrt(2) * sqrt( pixelSize/A_len ) + */ + + if (A_len > 0) + step = TConsts::sqrt2 * sqrt(pixelSize / A_len); + + return step; +} + +//----------------------------------------------------------------------------- + +double computeStep(const TThickQuadratic &quad, double pixelSize) +{ + TThickPoint + cp0 = quad.getThickP0(), + cp1 = quad.getThickP1(), + cp2 = quad.getThickP2(); + + TQuadratic + q1(TPointD(cp0.x, cp0.y), TPointD(cp1.x, cp1.y), TPointD(cp2.x, cp2.y)), + q2(TPointD(cp0.y, cp0.thick), TPointD(cp1.y, cp1.thick), TPointD(cp2.y, cp2.thick)), + q3(TPointD(cp0.x, cp0.thick), TPointD(cp1.x, cp1.thick), TPointD(cp2.x, cp2.thick)); + + return tmin(computeStep(q1, pixelSize), computeStep(q2, pixelSize), computeStep(q3, pixelSize)); +} + +//============================================================================= + +/* + Explanation: The length of a Bezier quadratic can be calculated explicitly. + + Let Q be the quadratic. The tricks to explicitly integrate | Q'(t) | are: + + - The integrand can be reformulated as: | Q'(t) | = sqrt(at^2 + bt + c); + - Complete the square beneath the sqrt (add/subtract sq(b) / 4a) + and perform a linear variable change. We reduce the integrand to: sqrt(kx^2 + k), + where k can be taken outside => sqrt(x^2 + 1) + - Use x = tan y. The integrand will yield sec^3 y. + - Integrate by parts. In short, the resulting primitive of sqrt(x^2 + 1) is: + + F(x) = ( x * sqrt(x^2 + 1) + log(x + sqrt(x^2 + 1)) ) / 2; +*/ + +void TQuadraticLengthEvaluator::setQuad(const TQuadratic &quad) +{ + const TPointD &p0 = quad.getP0(); + const TPointD &p1 = quad.getP1(); + const TPointD &p2 = quad.getP2(); + + TPointD speed0(2.0 * (p1 - p0)); + TPointD accel(2.0 * (p2 - p1) - speed0); + + double a = accel * accel; + double b = 2.0 * accel * speed0; + m_c = speed0 * speed0; + + m_constantSpeed = isAlmostZero(a); // => b isAlmostZero, too + if (m_constantSpeed) { + m_c = sqrt(m_c); + return; + } + + m_sqrt_a_div_2 = 0.5 * sqrt(a); + + m_noSpeed0 = isAlmostZero(m_c); // => b isAlmostZero, too + if (m_noSpeed0) + return; + + m_tRef = 0.5 * b / a; + double d = m_c - 0.5 * b * m_tRef; + + m_squareIntegrand = (d < TConsts::epsilon); + if (m_squareIntegrand) { + m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef); + return; + } + + m_e = d / a; + + double sqrt_part = sqrt(sq(m_tRef) + m_e); + double log_arg = m_tRef + sqrt_part; + + m_squareIntegrand = (log_arg < TConsts::epsilon); + if (m_squareIntegrand) { + m_f = (b > 0) ? -sq(m_tRef) : sq(m_tRef); + return; + } + + m_primitive_0 = m_sqrt_a_div_2 * (m_tRef * sqrt_part + m_e * log(log_arg)); +} + +//----------------------------------------------------------------------------- + +double TQuadraticLengthEvaluator::getLengthAt(double t) const +{ + if (m_constantSpeed) + return m_c * t; + + if (m_noSpeed0) + return m_sqrt_a_div_2 * sq(t); + + if (m_squareIntegrand) { + double t_plus_tRef = t + m_tRef; + return m_sqrt_a_div_2 * (m_f + ((t_plus_tRef > 0) ? sq(t_plus_tRef) : -sq(t_plus_tRef))); + } + + double y = t + m_tRef; + double sqrt_part = sqrt(sq(y) + m_e); + double log_arg = y + sqrt_part; //NOTE: log_arg >= log_arg0 >= TConsts::epsilon + + return m_sqrt_a_div_2 * (y * sqrt_part + m_e * log(log_arg)) - m_primitive_0; +} + +//----------------------------------------------------------------------------- +// End Of File +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tgeometry/tgeometry.cpp b/toonz/sources/common/tgeometry/tgeometry.cpp new file mode 100644 index 0000000..932a31f --- /dev/null +++ b/toonz/sources/common/tgeometry/tgeometry.cpp @@ -0,0 +1,241 @@ + + +#include "tgeometry.h" +#ifdef LINUX +#include +#endif + +using namespace std; + +const T3DPointD TConsts::nap3d((numeric_limits::max)(), + (numeric_limits::max)(), + (numeric_limits::max)()); + +const TThickPoint TConsts::natp((numeric_limits::max)(), + (numeric_limits::max)(), + (numeric_limits::max)()); + +const TPointD TConsts::napd((numeric_limits::max)(), + (numeric_limits::max)()); + +const TPointI TConsts::nap((numeric_limits::max)(), + (numeric_limits::max)()); + +const TRectD TConsts::infiniteRectD(-(numeric_limits::max)(), + -(numeric_limits::max)(), + (numeric_limits::max)(), + (numeric_limits::max)()); + +const TRectI TConsts::infiniteRectI(-(numeric_limits::max)(), + -(numeric_limits::max)(), + (numeric_limits::max)(), + (numeric_limits::max)()); + +//================================================================================================== + +// operazioni fra affini +TAffine &TAffine::operator=(const TAffine &a) +{ + a11 = a.a11; + a12 = a.a12; + a13 = a.a13; + a21 = a.a21; + a22 = a.a22; + a23 = a.a23; + return *this; +} +//-------------------------------------------------------------------------------------------------- +TAffine TAffine::operator*(const TAffine &b) const +{ + return TAffine( + a11 * b.a11 + a12 * b.a21, + a11 * b.a12 + a12 * b.a22, + a11 * b.a13 + a12 * b.a23 + a13, + + a21 * b.a11 + a22 * b.a21, + a21 * b.a12 + a22 * b.a22, + a21 * b.a13 + a22 * b.a23 + a23); +} +//-------------------------------------------------------------------------------------------------- +TAffine TAffine::operator*=(const TAffine &b) +{ + return *this = *this * b; +} +//-------------------------------------------------------------------------------------------------- +TAffine TAffine::inv() const +{ + if (a12 == 0.0 && a21 == 0.0) { + assert(a11 != 0.0); + assert(a22 != 0.0); + double inv_a11 = 1.0 / a11; + double inv_a22 = 1.0 / a22; + return TAffine(inv_a11, 0, -a13 * inv_a11, + 0, inv_a22, -a23 * inv_a22); + } else if (a11 == 0.0 && a22 == 0.0) { + assert(a12 != 0.0); + assert(a21 != 0.0); + double inv_a21 = 1.0 / a21; + double inv_a12 = 1.0 / a12; + return TAffine(0, inv_a21, -a23 * inv_a21, + inv_a12, 0, -a13 * inv_a12); + } else { + double d = 1. / det(); + return TAffine(a22 * d, -a12 * d, (a12 * a23 - a22 * a13) * d, + -a21 * d, a11 * d, (a21 * a13 - a11 * a23) * d); + } +} +//-------------------------------------------------------------------------------------------------- +double TAffine::det() const +{ + return a11 * a22 - a12 * a21; +} + +//-------------------------------------------------------------------------------------------------- + +//Confronti tra affini +bool TAffine::operator==(const TAffine &a) const +{ + return a11 == a.a11 && a12 == a.a12 && a13 == a.a13 && + a21 == a.a21 && a22 == a.a22 && a23 == a.a23; +} +//-------------------------------------------------------------------------------------------------- +bool TAffine::operator!=(const TAffine &a) const +{ + return a11 != a.a11 || a12 != a.a12 || a13 != a.a13 || + a21 != a.a21 || a22 != a.a22 || a23 != a.a23; +} +//-------------------------------------------------------------------------------------------------- +bool TAffine::isIdentity(double err) const +{ + return ((a11 - 1.0) * (a11 - 1.0) + (a22 - 1.0) * (a22 - 1.0) + + a12 * a12 + a13 * a13 + a21 * a21 + a23 * a23) < err; +} +//-------------------------------------------------------------------------------------------------- +bool TAffine::isTranslation(double err) const +{ + return ((a11 - 1.0) * (a11 - 1.0) + (a22 - 1.0) * (a22 - 1.0) + + a12 * a12 + a21 * a21) < err; +} +//-------------------------------------------------------------------------------------------------- +bool TAffine::isIsotropic(double err) const +{ + return areAlmostEqual(a11, a22, err) && areAlmostEqual(a12, -a21, err); +} + +//-------------------------------------------------------------------------------------------------- + +// applicazione +TPointD TAffine::operator*(const TPointD &p) const +{ + return TPointD(p.x * a11 + p.y * a12 + a13, p.x * a21 + p.y * a22 + a23); +} + +//-------------------------------------------------------------------------------------------------- + +TRectD TAffine::operator*(const TRectD &rect) const +{ + if (rect != TConsts::infiniteRectD) { + TPointD p1 = *this * rect.getP00(), + p2 = *this * rect.getP01(), + p3 = *this * rect.getP10(), + p4 = *this * rect.getP11(); + return TRectD(tmin(p1.x, p2.x, p3.x, p4.x), tmin(p1.y, p2.y, p3.y, p4.y), + tmax(p1.x, p2.x, p3.x, p4.x), tmax(p1.y, p2.y, p3.y, p4.y)); + } else + return TConsts::infiniteRectD; +} + +//-------------------------------------------------------------------------------------------------- + +TAffine TAffine::place(double u, double v, double x, double y) const +{ + return TAffine(a11, a12, x - (a11 * u + a12 * v), + a21, a22, y - (a21 * u + a22 * v)); +} + +//-------------------------------------------------------------------------------------------------- + +TAffine TAffine::place(const TPointD &pIn, const TPointD &pOut) const +{ + return TAffine(a11, a12, pOut.x - (a11 * pIn.x + a12 * pIn.y), + a21, a22, pOut.y - (a21 * pIn.x + a22 * pIn.y)); +} + +//================================================================================================== + +TRotation::TRotation(double degrees) +{ + double rad, sn, cs; + int idegrees = (int)degrees; + if ((double)idegrees == degrees && idegrees % 90 == 0) { + switch ((idegrees / 90) & 3) { + case 0: + sn = 0; + cs = 1; + break; + case 1: + sn = 1; + cs = 0; + break; + case 2: + sn = 0; + cs = -1; + break; + case 3: + sn = -1; + cs = 0; + break; + default: + sn = 0; + cs = 0; + break; + } + } else { + rad = degrees * (TConsts::pi_180); + sn = sin(rad); + cs = cos(rad); + if (sn == 1 || sn == -1) + cs = 0; + if (cs == 1 || cs == -1) + sn = 0; + } + a11 = cs; + a12 = -sn; + a21 = -a12; + a22 = a11; +} +//-------------------------------------------------------------------------------------------------- +TRotation::TRotation(const TPointD ¢er, double degrees) +{ + TAffine a = TTranslation(center) * TRotation(degrees) * TTranslation(-center); + a11 = a.a11; + a12 = a.a12; + a13 = a.a13; + a21 = a.a21; + a22 = a.a22; + a23 = a.a23; +} + +//================================================================================================== + +TScale::TScale(const TPointD ¢er, double sx, double sy) +{ + TAffine a = TTranslation(center) * TScale(sx, sy) * TTranslation(-center); + a11 = a.a11; + a12 = a.a12; + a13 = a.a13; + a21 = a.a21; + a22 = a.a22; + a23 = a.a23; +} +//-------------------------------------------------------------------------------------------------- +TScale::TScale(const TPointD ¢er, double s) +{ + TAffine a = TTranslation(center) * TScale(s) * TTranslation(-center); + a11 = a.a11; + a12 = a.a12; + a13 = a.a13; + a21 = a.a21; + a22 = a.a22; + a23 = a.a23; +} diff --git a/toonz/sources/common/tgl/tgl.cpp b/toonz/sources/common/tgl/tgl.cpp new file mode 100644 index 0000000..f345576 --- /dev/null +++ b/toonz/sources/common/tgl/tgl.cpp @@ -0,0 +1,718 @@ + + +#include "tgl.h" + +#include "tconvert.h" +#include "tofflinegl.h" +#include "trop.h" +#include "timage_io.h" +#include "tcurves.h" + +#ifndef __sgi +#ifdef WIN32 +#include +#else +#include +#endif +#endif + +#ifdef MACOSX +#include +#endif + +//#include "tthread.h" + +#undef SCALE_BY_GLU +//#undef NEW_DRAW_TEXT + +//----------------------------------------------------------------------------- + +namespace +{ + +// GLUquadric* localDisk=0; + +/* + Find the number of slices in function of radius size. + \par radius of circle + \par size of pixel + \ret number of division to obtain a circle + */ +int computeSlices(double radius, double pixelSize = 1.0) +{ + if (radius < 0) + return 2; + + double thetaStep; + double temp = pixelSize * 0.5 / radius; + + if (fabs(1.0 - temp) <= 1) + thetaStep = acos(1.0 - temp); + else + thetaStep = TConsts::pi_2 * 0.5; + + assert(thetaStep != 0.0); + + int numberOfSlices = (int)(2.0 * TConsts::pi / thetaStep); + + return numberOfSlices != 0 ? numberOfSlices : 2; +} +} // end of unnamed namespace + +//----------------------------------------------------------------------------- + +double tglGetPixelSize2() +{ + double mat[16]; + glMatrixMode(GL_MODELVIEW); + glGetDoublev(GL_MODELVIEW_MATRIX, mat); + + double det = fabs(mat[0] * mat[5] - mat[1] * mat[4]); + if (det < TConsts::epsilon) + det = TConsts::epsilon; + return 1.0 / det; +} + +//----------------------------------------------------------------------------- + +double tglGetTextWidth(const string &s, void *font) +{ + double factor = 0.07; + double w = 0; + for (int i = 0; i < (int)s.length(); i++) + w += glutStrokeWidth(font, s[i]); + return w * factor; +} + +//----------------------------------------------------------------------------- + +void tglDrawText(const TPointD &p, const string &s, void *character) +{ +#ifndef __sgi + glPushMatrix(); + glTranslated(p.x, p.y, 0); + double factor = 0.07; + glScaled(factor, factor, factor); + for (int i = 0; i < (int)s.size(); i++) + glutStrokeCharacter(character, s[i]); + glPopMatrix(); +#else + assert("Not Yet Implemented" && 0); + std::cout << s << std::endl; +#endif +} + +//----------------------------------------------------------------------------- + +void tglDrawText(const TPointD &p, const wstring &s, void *character) +{ +#ifndef __sgi + glPushMatrix(); + glTranslated(p.x, p.y, 0); + double factor = 0.07; + glScaled(factor, factor, factor); + for (int i = 0; i < (int)s.size(); i++) + glutStrokeCharacter(character, s[i]); + glPopMatrix(); +#else + assert("Not Yet Implemented" && 0); + std::cout << s << std::endl; +#endif +} + +//----------------------------------------------------------------------------- + +void tglDrawSegment(const TPointD &p1, const TPointD &p2) +{ + glBegin(GL_LINES); + tglVertex(p1); + tglVertex(p2); + glEnd(); +} + +//----------------------------------------------------------------------------- + +void tglDrawCircle(const TPointD ¢er, double radius) +{ + if (radius <= 0) + return; + + double pixelSize = 1; + int slices = 60; + + if (slices <= 0) + slices = computeSlices(radius, pixelSize) >> 1; + + double step = TConsts::pi / slices; + double step2 = 2.0 * step; + + double + cos_t, + sin_t, + cos_ts, sin_ts, t; + + glPushMatrix(); + glTranslated(center.x, center.y, 0.0); + glBegin(GL_LINES); + + cos_t = radius /* *1.0*/; + sin_t = 0.0; + for (t = 0; t + step < TConsts::pi_2; t += step2) { + cos_ts = radius * cos(t + step); + sin_ts = radius * sin(t + step); + + glVertex2f(cos_t, sin_t); + glVertex2f(cos_ts, sin_ts); + + glVertex2f(-cos_t, sin_t); + glVertex2f(-cos_ts, sin_ts); + + glVertex2f(-cos_t, -sin_t); + glVertex2f(-cos_ts, -sin_ts); + + glVertex2f(cos_t, -sin_t); + glVertex2f(cos_ts, -sin_ts); + + cos_t = cos_ts; + sin_t = sin_ts; + } + + cos_ts = 0.0; + sin_ts = radius /* *1.0*/; + + glVertex2f(cos_t, sin_t); + glVertex2f(cos_ts, sin_ts); + + glVertex2f(-cos_t, sin_t); + glVertex2f(-cos_ts, sin_ts); + + glVertex2f(-cos_t, -sin_t); + glVertex2f(-cos_ts, -sin_ts); + + glVertex2f(cos_t, -sin_t); + glVertex2f(cos_ts, -sin_ts); + + glEnd(); + glPopMatrix(); +} + +//----------------------------------------------------------------------------- + +void tglDrawDisk(const TPointD &c, double r) +{ + if (r <= 0) + return; + + double pixelSize = 1; + int slices = 60; + + if (slices <= 0) + slices = computeSlices(r, pixelSize) >> 1; + + glPushMatrix(); + glTranslated(c.x, c.y, 0.0); + GLUquadric *quadric = gluNewQuadric(); + gluDisk(quadric, 0, r, slices, 1); + gluDeleteQuadric(quadric); + glPopMatrix(); +} + +//----------------------------------------------------------------------------- + +void tglDrawRect(const TRectD &rect) +{ + glBegin(GL_LINE_LOOP); + tglVertex(rect.getP00()); + tglVertex(rect.getP10()); + tglVertex(rect.getP11()); + tglVertex(rect.getP01()); + glEnd(); +} + +//----------------------------------------------------------------------------- + +void tglFillRect(const TRectD &rect) +{ + glBegin(GL_POLYGON); + tglVertex(rect.getP00()); + tglVertex(rect.getP10()); + tglVertex(rect.getP11()); + tglVertex(rect.getP01()); + glEnd(); +} +//----------------------------------------------------------------------------- + +void tglRgbOnlyColorMask() +{ + tglMultColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + tglEnableBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +//----------------------------------------------------------------------------- + +void tglAlphaOnlyColorMask() +{ + tglMultColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +} + +//----------------------------------------------------------------------------- + +void tglEnableBlending(GLenum src, GLenum dst) +{ + glEnable(GL_BLEND); + glBlendFunc(src, dst); +} + +//----------------------------------------------------------------------------- + +void tglEnableLineSmooth(bool enable, double lineSize) +{ + if (enable) { + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glEnable(GL_LINE_SMOOTH); + glLineWidth(lineSize); + } else + glDisable(GL_LINE_SMOOTH); +} + +//----------------------------------------------------------------------------- + +void tglEnablePointSmooth(double pointSize) +{ + glEnable(GL_BLEND); + glPointSize(pointSize); +} + +//----------------------------------------------------------------------------- + +void tglGetColorMask(GLboolean &red, GLboolean &green, GLboolean &blue, GLboolean &alpha) +{ + GLboolean channels[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, &channels[0]); + red = channels[0], green = channels[1], blue = channels[2], alpha = channels[3]; +} + +//----------------------------------------------------------------------------- + +void tglMultColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +{ + GLboolean channels[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, &channels[0]); + glColorMask(red && channels[0], + green && channels[1], + blue && channels[2], + alpha && channels[3]); +} + +//============================================================================ + +namespace +{ +//============================================================================ + +class GlFontManager +{ + GlFontManager(); + +public: + ~GlFontManager(); + static GlFontManager *instance(); + bool setFont(void *font = GLUT_BITMAP_TIMES_ROMAN_10); + void drawText(/*const TRectD bBox,*/ + wstring wtext /*, + TDimensionD scale = TDimensionD(1.0, 1.0)*/); + +private: + static GlFontManager *m_instance; + + // font font_height + // | | + std::map m_fonts; + std::vector m_charsBBox; + void *m_currentFont; + TRaster32P m_fontTexture; + GLuint m_base; +}; + +//---------------------------------------------------------------------------- + +GlFontManager *GlFontManager::m_instance = 0L; + +//---------------------------------------------------------------------------- + +GlFontManager::GlFontManager() + : m_currentFont(0L), m_base(0) +{ + m_fonts.insert(std::make_pair(GLUT_BITMAP_8_BY_13, 13.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_9_BY_15, 15.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_TIMES_ROMAN_10, 10.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_TIMES_ROMAN_24, 24.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_HELVETICA_10, 10.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_HELVETICA_12, 12.0)); + m_fonts.insert(std::make_pair(GLUT_BITMAP_HELVETICA_18, 18.0)); + bool ok = setFont(); + assert(ok); +} + +//---------------------------------------------------------------------------- + +GlFontManager::~GlFontManager() +{ + m_instance = 0L; +} + +//---------------------------------------------------------------------------- + +GlFontManager *GlFontManager::instance() +{ + if (!m_instance) + m_instance = new GlFontManager(); + + return m_instance; +} + +//---------------------------------------------------------------------------- + +bool GlFontManager::setFont(void *font) +{ + // cerca il font scelto nella mappa dei fonts conosciuti + std::map::iterator it = + m_fonts.find(font); + + // se e' stato trovato + if (it != m_fonts.end()) { + m_currentFont = font; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushMatrix(); + m_base = glGenLists(256); + glListBase(m_base); + int i = 0; + for (; i < 256; ++i) { + glNewList(m_base + i, GL_COMPILE); + glutStrokeCharacter(GLUT_STROKE_ROMAN, i); + //glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, i); + glEndList(); + } + glPopAttrib(); + glPopMatrix(); + return true; + } + return false; +} + +//---------------------------------------------------------------------------- + +void GlFontManager::drawText(/*const TRectD bBox,*/ + wstring wtext /*, + TDimensionD scale*/) +{ + if (!m_currentFont) + return; + + string text = toString(wtext); + const char *textString = text.c_str(); + glListBase(m_base); + /* + glPushMatrix(); + glTranslated(bBox.x0, bBox.y0, 0.0); + glScaled(scale.lx*0.07, scale.ly*0.07, 1.0); + */ + glCallLists((GLuint)strlen(textString), GL_BYTE, textString); + /*glPopMatrix();*/ +} + +//============================================================================ + +} // anonymous namespace + +//============================================================================ + +void tglDraw(const TCubic &cubic, int precision, GLenum pointOrLine) +{ + CHECK_ERRORS_BY_GL; + assert(pointOrLine == GL_POINT || pointOrLine == GL_LINE); + float(*ctrlPts)[3]; + ctrlPts = new float[4][3]; + + ctrlPts[0][0] = cubic.getP0().x; + ctrlPts[0][1] = cubic.getP0().y; + ctrlPts[0][2] = 0.0; + + ctrlPts[1][0] = cubic.getP1().x; + ctrlPts[1][1] = cubic.getP1().y; + ctrlPts[1][2] = 0.0; + + ctrlPts[2][0] = cubic.getP2().x; + ctrlPts[2][1] = cubic.getP2().y; + ctrlPts[2][2] = 0.0; + + ctrlPts[3][0] = cubic.getP3().x; + ctrlPts[3][1] = cubic.getP3().y; + ctrlPts[3][2] = 0.0; + + CHECK_ERRORS_BY_GL; + glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlPts[0][0]); + CHECK_ERRORS_BY_GL; + glEnable(GL_MAP1_VERTEX_3); + CHECK_ERRORS_BY_GL; + glMapGrid1f(precision, 0.0, 1.0); + CHECK_ERRORS_BY_GL; + glEvalMesh1(pointOrLine, 0, precision); + CHECK_ERRORS_BY_GL; + + delete[] ctrlPts; +} + +//----------------------------------------------------------------------------- + +void tglDraw(const TRectD &rect, + const std::vector &textures, + bool blending) +{ + double pixelSize2 = tglGetPixelSize2(); + // level e' la minore potenza di 2 maggiore di sqrt(pixelSize2) + unsigned int level = 1; + while (pixelSize2 * level * level <= 1.0) + level <<= 1; + + unsigned int texturesCount = (int)textures.size(); + if (level > texturesCount) + level = texturesCount; + + level = texturesCount - level; + + tglDraw(rect, textures[level], blending); +} + +//------------------------------------------------------------------- + +void tglDraw(const TRectD &rect, const TRaster32P &tex, bool blending) +{ + CHECK_ERRORS_BY_GL; + glPushAttrib(GL_ALL_ATTRIB_BITS); + if (blending) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + unsigned int texWidth = 1; + unsigned int texHeight = 1; + + while (texWidth < (unsigned int)tex->getLx()) + texWidth = texWidth << 1; + + while (texHeight < (unsigned int)tex->getLy()) + texHeight = texHeight << 1; + + double lwTex = 1.0; + double lhTex = 1.0; + + TRaster32P texture; + unsigned int texLx = (unsigned int)tex->getLx(); + unsigned int texLy = (unsigned int)tex->getLy(); + + if (texWidth != texLx || + texHeight != texLy) { + texture = TRaster32P(texWidth, texHeight); + texture->fill(TPixel32(0, 0, 0, 0)); + texture->copy(tex); + lwTex = (texLx) / (double)(texWidth); + lhTex = (texLy) / (double)(texHeight); + if (lwTex > 1.0) + lwTex = 1.0; + if (lhTex > 1.0) + lhTex = 1.0; + } else + texture = tex; + GLenum fmt = +#ifdef TNZ_MACHINE_CHANNEL_ORDER_BGRM + GL_BGRA_EXT; +#elif TNZ_MACHINE_CHANNEL_ORDER_MBGR + GL_ABGR_EXT; +#elif TNZ_MACHINE_CHANNEL_ORDER_RGBM + GL_RGBA; +#elif TNZ_MACHINE_CHANNEL_ORDER_MRGB + GL_BGRA; +#else + @unknow channel order +// Error PLATFORM NOT SUPPORTED +#endif + + // Generate a texture id and bind it. + GLuint texId; + glGenTextures(1, &texId); + + glBindTexture(GL_TEXTURE_2D, texId); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->getWrap()); + + texture->lock(); + glTexImage2D(GL_TEXTURE_2D, + 0, + 4, + texWidth, + texHeight, + 0, + fmt, +#ifdef TNZ_MACHINE_CHANNEL_ORDER_MRGB + GL_UNSIGNED_INT_8_8_8_8_REV, +#else + GL_UNSIGNED_BYTE, +#endif + texture->getRawData()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + double rectLx = rect.getLx(); + double rectLy = rect.getLy(); + + tglColor(TPixel32(0, 0, 0, 0)); + + glPushMatrix(); + + glTranslated(rect.x0, rect.y0, 0.0); + glBegin(GL_POLYGON); + + glTexCoord2d(0, 0); + tglVertex(TPointD(0.0, 0.0)); + + glTexCoord2d(lwTex, 0); + tglVertex(TPointD(rectLx, 0.0)); + + glTexCoord2d(lwTex, lhTex); + tglVertex(TPointD(rectLx, rectLy)); + + glTexCoord2d(0, lhTex); + tglVertex(TPointD(0.0, rectLy)); + + glEnd(); + glDisable(GL_TEXTURE_2D); + + glPopMatrix(); + glPopAttrib(); + + // Delete texture + glDeleteTextures(1, &texId); + + texture->unlock(); +} + +//----------------------------------------------------------------------------- + +void tglBuildMipmaps(std::vector &rasters, + const TFilePath &filepath) +{ + assert(rasters.size() > 0); + TRop::ResampleFilterType resampleFilter = TRop::ClosestPixel; + TRasterP ras; + TImageReader::load(filepath, ras); + int rasLx = ras->getLx(); + int rasLy = ras->getLy(); + + int lx = 1; + while (lx < rasLx) + lx <<= 1; + + int ly = 1; + while (ly < rasLy) + ly <<= 1; + + TRaster32P ras2(lx, ly); + double sx = (double)lx / (double)ras->getLx(); + double sy = (double)ly / (double)ras->getLy(); +#ifndef SCALE_BY_GLU + TRop::resample(ras2, ras, TScale(sx, sy), resampleFilter); +#else + ras->lock(); + gluScaleImage(GL_RGBA, ras->getLx(), ras->getLy(), GL_UNSIGNED_BYTE, ras->getRawData(), + lx, ly, GL_UNSIGNED_BYTE, ras2->getRawData()); + ras->unlock(); +#endif + + rasters[0] = ras2; + int ras2Lx = ras2->getLx(); + int ras2Ly = ras2->getLy(); + for (int i = 1; i < (int)rasters.size(); ++i) { + lx >>= 1; + ly >>= 1; + if (lx < 1) + lx = 1; + if (ly < 1) + ly = 1; + rasters[i] = TRaster32P(lx, ly); + sx = (double)lx / (double)ras2Lx; + sy = (double)ly / (double)ras2Ly; + rasters[i] = TRaster32P(lx, ly); +#ifndef SCALE_BY_GLU + TRop::resample(rasters[i], ras2, TScale(sx, sy), resampleFilter); +#else + ras2->lock(); + gluScaleImage(GL_RGBA, ras->getLx(), ras->getLy(), GL_UNSIGNED_BYTE, ras2->getRawData(), + lx, ly, GL_UNSIGNED_BYTE, rasters[i]->getRawData()); + ras2->unlock(); +#endif + } +} + +//----------------------------------------------------------------------------- +//Forse si potrebbe togliere l'ifdef ed usare QT +#if defined(WIN32) + +TGlContext tglGetCurrentContext() +{ + return std::make_pair(wglGetCurrentDC(), wglGetCurrentContext()); +} + +void tglMakeCurrent(TGlContext context) +{ + wglMakeCurrent(context.first, context.second); +} + +void tglDoneCurrent(TGlContext) +{ + wglMakeCurrent(NULL, NULL); +} + +#elif defined(LINUX) || defined(__sgi) || defined(MACOSX) + +TGlContext tglGetCurrentContext() +{ + return reinterpret_cast( + const_cast(QGLContext::currentContext())); + + // (Daniele) I'm not sure why QGLContext::currentContext() returns + // const. I think it shouldn't, and guess (hope) this is safe... +} + +void tglMakeCurrent(TGlContext context) +{ + if (context) + reinterpret_cast(context)->makeCurrent(); + else + tglDoneCurrent(tglGetCurrentContext()); +} + +void tglDoneCurrent(TGlContext context) +{ + if (context) + reinterpret_cast(context)->doneCurrent(); +} + +#elif @unknow platform ! + +#endif + +//----------------------------------------------------------------------------- +// End Of File +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tgl/tgldisplaylistsmanager.cpp b/toonz/sources/common/tgl/tgldisplaylistsmanager.cpp new file mode 100644 index 0000000..123ff91 --- /dev/null +++ b/toonz/sources/common/tgl/tgldisplaylistsmanager.cpp @@ -0,0 +1,102 @@ + + +// TnzCore includes +#include "tsmartpointer.h" + +// tcg includes +#include "tcg/tcg_list.h" + +#include "tgldisplaylistsmanager.h" + +//*********************************************************************************************** +// Local namespace - declarations +//*********************************************************************************************** + +namespace +{ + +struct ProxyReference { + TGLDisplayListsProxy *m_proxy; + int m_refCount; + + ProxyReference(TGLDisplayListsProxy *proxy) : m_proxy(proxy), m_refCount() {} +}; + +} // namespace + +//*********************************************************************************************** +// Local namespace - globals +//*********************************************************************************************** + +namespace +{ + +tcg::list m_proxies; +std::map m_proxyIdsByContext; + +} // namespace + +//*********************************************************************************************** +// TGLDisplayListsManager implementation +//*********************************************************************************************** + +TGLDisplayListsManager *TGLDisplayListsManager::instance() +{ + static TGLDisplayListsManager theInstance; + return &theInstance; +} + +//----------------------------------------------------------------------------------- + +int TGLDisplayListsManager::storeProxy(TGLDisplayListsProxy *proxy) +{ + return m_proxies.push_back(ProxyReference(proxy)); +} + +//----------------------------------------------------------------------------------- + +void TGLDisplayListsManager::attachContext(int dlSpaceId, TGlContext context) +{ + m_proxyIdsByContext.insert(std::make_pair(context, dlSpaceId)); + ++m_proxies[dlSpaceId].m_refCount; +} + +//----------------------------------------------------------------------------------- + +void TGLDisplayListsManager::releaseContext(TGlContext context) +{ + std::map::iterator it = m_proxyIdsByContext.find(context); + + assert(it != m_proxyIdsByContext.end()); + if (it == m_proxyIdsByContext.end()) + return; + + int dlSpaceId = it->second; + if (--m_proxies[dlSpaceId].m_refCount <= 0) { + // Notify observers first + observers_container::const_iterator ot, oEnd = observers().end(); + for (ot = observers().begin(); ot != oEnd; ++ot) + static_cast(*ot)->onDisplayListDestroyed(dlSpaceId); + + // Then, destroy stuff + delete m_proxies[dlSpaceId].m_proxy; + m_proxies.erase(dlSpaceId); + } + + m_proxyIdsByContext.erase(it); +} + +//----------------------------------------------------------------------------------- + +int TGLDisplayListsManager::displayListsSpaceId(TGlContext context) +{ + std::map::iterator it = m_proxyIdsByContext.find(context); + return (it == m_proxyIdsByContext.end()) ? -1 : it->second; +} + +//----------------------------------------------------------------------------------- + +TGLDisplayListsProxy *TGLDisplayListsManager::dlProxy(int dlSpaceId) +{ + return m_proxies[dlSpaceId].m_proxy; +} diff --git a/toonz/sources/common/tgl/tstencilcontrol.cpp b/toonz/sources/common/tgl/tstencilcontrol.cpp new file mode 100644 index 0000000..57aaaeb --- /dev/null +++ b/toonz/sources/common/tgl/tstencilcontrol.cpp @@ -0,0 +1,378 @@ + + +#include "tstencilcontrol.h" +#include "tgl.h" +#include "tthreadmessage.h" +#include + +#include + +//----------------------------------------------------------------------------------------- + +namespace +{ + +//singleton +class StencilControlManager +{ + + QThreadStorage m_storage; + + StencilControlManager() {} + +public: + static StencilControlManager *instance() + { + static StencilControlManager theInstance; + return &theInstance; + } + + TStencilControl *getCurrentStencilControl() + { + if (!m_storage.hasLocalData()) { + m_storage.setLocalData(new TStencilControl); + } + + return m_storage.localData(); + } + + ~StencilControlManager() + { + } +}; + +} //Local namepace + +//----------------------------------------------------------------------------------------- + +class TStencilControl::Imp +{ +public: + int m_stencilBitCount; + int m_pushCount; + int m_currentWriting; //current stencil bit plane. + //0 is the first bit plane ; -1 menas no stencil mask is writing + + int m_virtualState; +//the state of the (eventually virtual) top mask. +//A mask is virtual if overflows stencil buffer +//0 is closed and disabled, 1 closed and enabled and 2 is opened + +#ifdef _DEBUG + std::stack fullState; +// state of each mask in the stack (except top mask). +// 'true' means opend; 'false' means close and enabled +// Used only for assert +#endif + + unsigned char m_writingMask; //bit mask. The i-th bit=1 iff the i-th mask is opened to write + unsigned char m_drawOnScreenMask; //bitmsk.The ith bit=1 iff the ith mask WRITE also ON SCREEN WHEN WRITE ON STENCIL BIT PLANE + unsigned char m_enabledMask; //bit mask. The i-th bit=1 iff the i-th mask is enabled + unsigned char m_inOrOutMask; //bit mask. The i-th bit=1 iff the i-th mask is inside + unsigned char m_drawOnlyOnceMask; + + Imp(); + + void updateOpenGlState(); + + void pushMask(); + // make a new stencil plane the current one. + // if there is no plane available, increase a counter and does not push (virtual masks) + // So the same number of pop has no effect + + void popMask(); + // assert if the stencil stack contains only 0 + + void beginMask(DrawMode drawMode); + // clear the current stencil plane; start writing to it + // if drawOnScreen is not 0, it writes also to the color buffer (or stencil plane if another + // mask is open). If drawOnScreen is 2, it drows only once (by stencil buffer) + + void endMask(); + // end writing to the stencil plane. + + void enableMask(MaskType maskType); + void disableMask(); + // between enableMask()/disableMask() drawing is filtered by the values of the current + // stencil plane + // n.b. enableMask()/disableMask() can be nidified. Between the inner pair writing is enabled + // according to the AND of all of them +}; + +//----------------------------------------------------------------------------------------- + +TStencilControl::Imp::Imp() + : m_stencilBitCount(0), m_pushCount(1), m_currentWriting(-1), m_enabledMask(0), m_writingMask(0), m_inOrOutMask(0), m_drawOnScreenMask(0), m_drawOnlyOnceMask(0), m_virtualState(0) +{ + glGetIntegerv(GL_STENCIL_BITS, (GLint *)&m_stencilBitCount); + + glStencilMask(0xFFFFFFFF); + //glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); +} + +//--------------------------------------------------------- + +TStencilControl *TStencilControl::instance() +{ + StencilControlManager *instance = StencilControlManager::instance(); + return instance->getCurrentStencilControl(); +} + +//--------------------------------------------------------- + +TStencilControl::TStencilControl() + : m_imp(new Imp) +{ +} + +//--------------------------------------------------------- + +TStencilControl::~TStencilControl() +{ + delete m_imp; +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::updateOpenGlState() +{ + if (m_currentWriting >= 0) { //writing on stencil buffer + unsigned char currentWritingMask = 1 << m_currentWriting; + + bool drawOnlyOnce = (currentWritingMask & m_drawOnlyOnceMask) != 0; + + if (currentWritingMask & m_drawOnScreenMask) { + unsigned char lastWritingMask; + int lastWriting = m_currentWriting - 1; + for (; lastWriting >= 0; lastWriting--) { + lastWritingMask = 1 << lastWriting; + if ((lastWritingMask & m_writingMask) == lastWritingMask) + break; + } + if (lastWriting < 0) { + //glColorMask(1,1,1,1); + + if (drawOnlyOnce) + m_enabledMask |= currentWritingMask; + else + m_enabledMask &= ~currentWritingMask; + } else { + tglMultColorMask(0, 0, 0, 0); + //glDrawBuffer(GL_NONE); + + drawOnlyOnce = false; //essendo solo un'effetto visivo, se sto scrivendo su una maschera e non a schermo, e' inutile + currentWritingMask |= lastWritingMask; + } + } else + tglMultColorMask(0, 0, 0, 0); + //glDrawBuffer(GL_NONE); + + glStencilMask(currentWritingMask); + + if (drawOnlyOnce) { + glStencilFunc(GL_EQUAL, m_inOrOutMask, m_enabledMask); + glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + } else { + glStencilFunc(GL_EQUAL, currentWritingMask | m_inOrOutMask, m_enabledMask); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + } + + } else { //writing on screen buffer + glStencilMask(0xFFFFFFFF); + glStencilFunc(GL_EQUAL, m_inOrOutMask, m_enabledMask); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + //glColorMask(1,1,1,1); + } + + if (!m_enabledMask && m_currentWriting < 0) + glDisable(GL_STENCIL_TEST); + else + glEnable(GL_STENCIL_TEST); +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::pushMask() +{ + m_pushCount++; +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::beginMask(DrawMode drawMode) +{ + m_currentWriting = m_pushCount - 1; + unsigned char currentMask = 1 << m_currentWriting; + + m_writingMask |= currentMask; + + if (drawMode == DRAW_ALSO_ON_SCREEN) { + m_drawOnScreenMask |= currentMask; + } else if (drawMode == DRAW_ON_SCREEN_ONLY_ONCE) { + m_drawOnScreenMask |= currentMask; + m_drawOnlyOnceMask |= currentMask; + } else { + m_drawOnScreenMask &= ~currentMask; + m_drawOnlyOnceMask &= ~currentMask; + } + + glEnable(GL_STENCIL_TEST); + glStencilMask(currentMask); // enabled to modify only current bitPlane + glClear(GL_STENCIL_BUFFER_BIT); // and clear it + updateOpenGlState(); +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::endMask() +{ + assert(m_pushCount - 1 == m_currentWriting); + + unsigned char currentMask = 1 << (m_pushCount - 1); + + m_writingMask &= ~currentMask; //stop writing + m_enabledMask &= ~currentMask; + m_drawOnScreenMask &= ~currentMask; + m_drawOnlyOnceMask &= ~currentMask; + + //---------------------------------------------------------- + + m_currentWriting--; + for (; m_currentWriting >= 0; m_currentWriting--) { + unsigned char currentWritingMask = 1 << m_currentWriting; + if ((currentWritingMask & m_writingMask) == currentWritingMask) + break; + } + + updateOpenGlState(); +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::enableMask(MaskType maskType) +{ + unsigned char currentMask = 1 << (m_pushCount - 1); + + if ((m_enabledMask & currentMask) == 0) + glPushAttrib(GL_ALL_ATTRIB_BITS); + + m_enabledMask |= currentMask; + + assert(maskType == SHOW_INSIDE || maskType == SHOW_OUTSIDE); + + if (maskType == SHOW_INSIDE) + m_inOrOutMask |= currentMask; + else + m_inOrOutMask &= ~currentMask; + + updateOpenGlState(); +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::disableMask() +{ + unsigned char currentMask = 1 << (m_pushCount - 1); + + m_enabledMask &= ~currentMask; + m_inOrOutMask &= ~currentMask; + + updateOpenGlState(); + glPopAttrib(); +} + +//--------------------------------------------------------- + +void TStencilControl::Imp::popMask() +{ + --m_pushCount; + assert(m_pushCount > 0); //there is at least one mask in the stack +} + +//--------------------------------------------------------- +//--------------------------------------------------------- + +//questo forse e' un po' brutto: +//La maschera e' chiusa. +//Se e' abilitata, open = push+open +//Se e' disabilitata, open = open + +void TStencilControl::beginMask(DrawMode drawMode) +{ + glPushAttrib(GL_ALL_ATTRIB_BITS); + + if (m_imp->m_virtualState) //opened or enabled + { +#ifdef _DEBUG + m_imp->fullState.push(m_imp->m_virtualState == 2); +#endif + + m_imp->pushMask(); + } + + m_imp->m_virtualState = 2; //opened + + if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) + m_imp->beginMask(drawMode); +} + +//--------------------------------------------------------- + +void TStencilControl::endMask() +{ + if (!m_imp->m_virtualState) //closed and disabled + { + m_imp->popMask(); + +#ifdef _DEBUG + assert(m_imp->fullState.top()); //the state before last push must be opened + m_imp->fullState.pop(); +#endif + } + + assert(m_imp->m_virtualState != 1); //yet closed + + m_imp->m_virtualState = 0; + + if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) + m_imp->endMask(); + + glPopAttrib(); +} + +//--------------------------------------------------------- + +void TStencilControl::enableMask(MaskType maskType) +{ + assert(m_imp->m_virtualState != 2); //cannot enable an opened mask + + m_imp->m_virtualState = 1; //enabled + + if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) + m_imp->enableMask(maskType); +} + +//--------------------------------------------------------- + +void TStencilControl::disableMask() +{ + assert(m_imp->m_virtualState != 2); //cannot disable an opened mask + + if (!m_imp->m_virtualState) //closed and disabled + { + m_imp->popMask(); + +#ifdef _DEBUG + assert(!m_imp->fullState.top()); //the state before last push must be enabled + m_imp->fullState.pop(); +#endif + } + + m_imp->m_virtualState = 0; //close and disabled + + if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) + m_imp->disableMask(); +} + +//--------------------------------------------------------- diff --git a/toonz/sources/common/tgraphics.cpp b/toonz/sources/common/tgraphics.cpp new file mode 100644 index 0000000..3868f59 --- /dev/null +++ b/toonz/sources/common/tgraphics.cpp @@ -0,0 +1,313 @@ + + +#include "tgraphics.h" + +extern "C" { +#include "Tw/Gf.h" +#include "tvis.h" +} + +TGraphics::TGraphics(_TWIDGET *_gf, + int ras_x0, int ras_y0, + int ras_x1, int ras_y1, + int gf_x0, int gf_y0, + int gf_x1, int gf_y1, + int zoom_level) + : gf(_gf), currentPoint(0, 0), gfRegion(gf_x0, gf_y0, gf_x1, gf_y1), rasterRegion(ras_x0 - 1, ras_y0 - 1, ras_x1 + 1, ras_y1 + 1) + //, rasterRegion(ras_x0,ras_y0,ras_x1,ras_y1) + , + zoomFactor(1), pixelSize(1) +{ + double dx, dy; + int blx, bly; + + if (zoom_level > 0) + zoomFactor = 1 << zoom_level; + else if (zoom_level < 0) + zoomFactor = 1.0 / (1 << -zoom_level); + pixelSize = 1.0 / zoomFactor; + + blx = ras_x0 - gf_x0 / zoomFactor; + bly = ras_y0 - gf_y0 / zoomFactor; + dx = 0.5 - blx; + dy = 0.5 - bly; + + GfPushMatrix(gf); + GfTranslate(gf, -0.5, -0.5); + GfScale(gf, zoomFactor, zoomFactor); + GfTranslate(gf, dx, dy); +} + +//--------------------------------------------------- + +TGraphics::~TGraphics() +{ + GfPopMatrix(gf); +} + +//--------------------------------------------------- + +void TGraphics::setColor(int r, int g, int b) +{ + _r = r; + _g = g; + _b = b; + GfCpack(gf, r, g, b); +} + +//--------------------------------------------------- + +void TGraphics::drawLine(const TPointI &a, const TPointI &b) +{ + if (a.x < rasterRegion.x0 && b.x < rasterRegion.x0 || + a.x > rasterRegion.x1 && b.x > rasterRegion.x1 || + a.y < rasterRegion.y0 && b.y < rasterRegion.y0 || + a.y > rasterRegion.y1 && b.y > rasterRegion.y1) + return; + int v[2]; + GfBgnLine(gf); + v[0] = a.x; + v[1] = a.y; + GfV2i(gf, v); + v[0] = b.x; + v[1] = b.y; + GfV2i(gf, v); + GfEndLine(gf); +} + +//--------------------------------------------------- + +void TGraphics::drawLine(const TPointD &a, const TPointD &b) +{ + if (a.x < rasterRegion.x0 && b.x < rasterRegion.x0 || + a.x > rasterRegion.x1 && b.x > rasterRegion.x1 || + a.y < rasterRegion.y0 && b.y < rasterRegion.y0 || + a.y > rasterRegion.y1 && b.y > rasterRegion.y1) + return; + double v[2]; + GfBgnLine(gf); + v[0] = a.x; + v[1] = a.y; + GfV2d(gf, v); + v[0] = b.x; + v[1] = b.y; + GfV2d(gf, v); + GfEndLine(gf); +} + +//--------------------------------------------------- + +void TGraphics::beginPolygon() +{ + GfBgnPolygon(gf); +} + +//--------------------------------------------------- + +void TGraphics::endPolygon() +{ + GfEndPolygon(gf); +} + +//--------------------------------------------------- + +void TGraphics::beginLine() +{ + GfBgnLine(gf); +} + +//--------------------------------------------------- + +void TGraphics::endLine() +{ + GfEndLine(gf); +} + +//--------------------------------------------------- + +void TGraphics::vertex(const TPointI &a) +{ + int v[2]; + v[0] = a.x; + v[1] = a.y; + GfV2i(gf, v); +} + +//--------------------------------------------------- + +void TGraphics::vertex(const TPointD &a) +{ + double v[2]; + v[0] = a.x; + v[1] = a.y; + GfV2d(gf, v); +} + +//--------------------------------------------------- + +void TGraphics::drawRect(const TPointI &a, const TPointI &b) +{ + GfRecti(gf, a.x, a.y, b.x, b.y); +} + +//--------------------------------------------------- + +void TGraphics::drawRect(const TPointD &a, const TPointD &b) +{ + GfRect(gf, a.x, a.y, b.x, b.y); +} + +//--------------------------------------------------- + +void TGraphics::drawRect(const TRectI &rect) +{ + GfRecti(gf, rect.x0, rect.y0, rect.x1, rect.y1); +} + +//--------------------------------------------------- + +void TGraphics::drawRect(const TRectD &rect) +{ + GfRect(gf, rect.x0, rect.y0, rect.x1, rect.y1); +} + +//--------------------------------------------------- + +void TGraphics::drawArc(const TBezierArc &arc) +{ + int n = 50; + beginLine(); + for (int i = 0; i <= n; i++) + vertex(arc.getPoint((double)i / (double)n)); + endLine(); +} + +//--------------------------------------------------- + +void TGraphics::drawArc(const TCubicCurve &arc) +{ + int n = 80; + beginLine(); + for (int i = 0; i <= n; i++) + vertex(arc.getPoint((double)i / (double)n)); + endLine(); +} + +//--------------------------------------------------- + +void TGraphics::drawArcTo(const TPointD &d1, const TPointD &d2, + const TPointD &d3) +{ + TPointD oldPoint = currentPoint; + currentPoint += d1 + d2 + d3; + drawArc(TBezierArc(oldPoint, oldPoint + d1, oldPoint + d1 + d2, currentPoint)); +} + +//--------------------------------------------------- + +void TGraphics::drawDiamond(const TPointD &p, double r) +{ + beginPolygon(); + vertex(p + TPointD(r, 0)); + vertex(p + TPointD(0, r)); + vertex(p + TPointD(-r, 0)); + vertex(p + TPointD(0, -r)); + endPolygon(); +} + +//--------------------------------------------------- + +void TGraphics::drawCross(const TPointD &p, double r) +{ + drawLine(p - TPointD(r, r), p + TPointD(r, r)); + drawLine(p - TPointD(-r, r), p + TPointD(-r, r)); +} + +//--------------------------------------------------- + +void TGraphics::drawSquare(const TPointD &p, double r) +{ + beginLine(); + vertex(p + TPointD(-r, -r)); + vertex(p + TPointD(-r, r)); + vertex(p + TPointD(r, r)); + vertex(p + TPointD(r, -r)); + vertex(p + TPointD(-r, -r)); + endLine(); +} + +//--------------------------------------------------- + +void TGraphics::drawArc( + const TPointD &p0, + const TPointD &p1, + const TPointD &p2) +{ + TRectD rect = convert(rasterRegion.enlarge(+10)); + //TRectD rect(rasterRegion.x0, rasterRegion.y0, rasterRegion.x1, rasterRegion.y1); + TRectD bBox = boundingBox(p0, p1, p2); + + if (!rect.overlaps(bBox)) { + /* + unsigned char tmp_r = _r; + unsigned char tmp_g = _g; + unsigned char tmp_b = _b; + setColor(100,100,100); + drawRect(bBox); + drawLine(TLineD(bBox.x0, bBox.y0, bBox.x1, bBox.y1)); + drawLine(TLineD(bBox.x0, bBox.y1, bBox.x1, bBox.y0)); + setColor(tmp_r, tmp_g, tmp_b); + */ + return; + } + + double threshold = pixelSize * 0.125; + + TPointD v = p2 - p0; + TPointD u = p1 - p0; + TPointD r = rotate90(v); + + double sqr_tsh = (threshold * threshold) * (v * v); + double dist = r * u; + + if ((dist * dist) > sqr_tsh) { + TPointD l1 = 0.5 * (p0 + p1); + TPointD r1 = 0.5 * (p1 + p2); + TPointD l2 = 0.5 * (l1 + r1); + drawArc(p0, l1, l2); + drawArc(l2, r1, p2); + } else { + beginLine(); + vertex(p0); + vertex(p2); + endLine(); + } +} + +//--------------------------------------------------- + +void TGraphics::drawCircle(TPointD p, double radius) +{ + GfCirc(gf, p.x, p.y, radius); +} + +//--------------------------------------------------- + +void TGraphics::fillCircle(TPointD p, double radius) +{ + GfCircf(gf, p.x, p.y, radius); +} + +//--------------------------------------------------- + +void TGraphics::rectWrap(int wrap_pixels) +{ + GfRectWrap(gf, wrap_pixels); +} + +//--------------------------------------------------- + +void TGraphics::rectWrite(int x0, int y0, int x1, int y1, void *buffer) +{ + GfRectWrite(gf, x0, y0, x1, y1, buffer); +} diff --git a/toonz/sources/common/tiio/bmp/filebmp.c b/toonz/sources/common/tiio/bmp/filebmp.c new file mode 100644 index 0000000..c084a6b --- /dev/null +++ b/toonz/sources/common/tiio/bmp/filebmp.c @@ -0,0 +1,2457 @@ + + +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif + +#ifndef _DEBUG +#undef _STLP_DEBUG +#else +#define _STLP_DEBUG 1 +#endif + +#include +#include +#include +#include + +/* brutto! Forse dovremmo metterlo in include */ +#include "../image/compatibility/tnz4.h" +#include "../image/compatibility/inforegion.h" + +#include "filebmp.h" + +/* +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char UCHAR; +typedef unsigned int UINT; +typedef void IMAGERGB; +typedef struct {USHORT m,b,g,r;} SPIXEL; +#define CASE break; case +#define __OR ; case +#define DEFAULT break; default +#define TRUE 1 +#define FALSE 0 +#define NIL (void*)0 +*/ + +/* +typedef struct bmpimage { + int xsize,ysize; + LPIXEL *buffer; + BMP_SUBTYPE type; + } IMAGE; +*/ + +#define UNUSED_REDUCE_COLORS + +/*---------------------------------------------------------------------------*/ +/*-- Defines ----------------------------------------------------------------*/ + +#define BMP_BI_RGB 0 +#define BMP_BI_RLE8 1 +#define BMP_BI_RLE4 2 + +#define BMP_WIN_NEW 40 +#define BMP_OS2_NEW 64 + +#define BMP_READ_INFO 1 +#define BMP_READ_IMAGE 2 + +#define BMP_FERROR(fp) (ferror(fp) || feof(fp)) + +#define BMP_RMAP(x) (x & 0xe0) | ((x & 0xe0) >> 3) | ((x & 0xc0) >> 6) +#define BMP_GMAP(x) ((x & 0x1c) << 3) | (x & 0x1c) | ((x & 0x18) >> 3) +#define BMP_BMAP(x) ((x & 0x03) << 6) | ((x & 0x03) << 4) | ((x & 0x03) << 2) | (x & 0x03) + +#define BMP_RMAP16(x) ((x & 0xc0) << 0) | ((x & 0xc0) >> 2) | ((x & 0xc0) >> 4) | ((x & 0xc0) >> 6) +#define BMP_GMAP16(x) ((x & 0x60) << 1) | ((x & 0x60) >> 1) | ((x & 0x60) >> 3) | ((x & 0x60) >> 5) +#define BMP_BMAP16(x) ((x & 0x30) << 2) | ((x & 0x30) << 0) | ((x & 0x30) >> 2) | ((x & 0x30) >> 4) + +#define BMP_CUT(a, b, c) \ + { \ + if (a < b) \ + a = b; \ + else if (a > c) \ + a = c; \ + } + +#define BMP_REDUCE_COLORS(r, g, b) ((r & 0xe0) | ((g & 0xe0) >> 3) | ((b & 0xc0) >> 6)) + +#define BMP_ADD_ERROR(pix, weight) \ + { \ + tmp = (pix).r + ((r1 * weight) >> 4); \ + BMP_CUT(tmp, 0, 255); \ + (pix).r = tmp; \ + tmp = (pix).g + ((g1 * weight) >> 4); \ + BMP_CUT(tmp, 0, 255); \ + (pix).g = tmp; \ + tmp = (pix).b + ((b1 * weight) >> 4); \ + BMP_CUT(tmp, 0, 255); \ + (pix).b = tmp; \ + } + +#define BMP_ADD_ERROR_BW(pix, error) \ + { \ + tmp = (pix).r + (error); \ + BMP_CUT(tmp, 0, 255); \ + (pix).r = tmp; \ + tmp = (pix).g + (error); \ + BMP_CUT(tmp, 0, 255); \ + (pix).g = tmp; \ + tmp = (pix).b + (error); \ + BMP_CUT(tmp, 0, 255); \ + (pix).b = tmp; \ + } + +/*---------------------------------------------------------------------------*/ +/*-- Structures and Enums ---------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/*-- Prototypes -------------------------------------------------------------*/ + +int load_bmp_header(FILE *fp, BMP_HEADER **pHd); +int write_bmp_header(FILE *fp, BMP_HEADER *hd); +void release_bmp_header(BMP_HEADER *hd); + +int write_bmp_palette(FILE *fp, int nc, UCHAR *b, UCHAR *g, UCHAR *r); + +int make_bmp_palette(int colors, int grey, UCHAR *r, UCHAR *g, UCHAR *b); + +BMP_SUBTYPE bmp_get_colorstyle(IMAGE *img); + +int error_checking_bmp(BMP_HEADER *hd); + +int read_bmp_line(FILE *fp, void *line, + UINT w, UINT h, UCHAR **map, BMP_SUBTYPE type); + +int write_bmp_line(FILE *fp, void *line_buffer, + UINT w, UINT row, UCHAR *map, BMP_SUBTYPE type); + +int skip_bmp_lines(FILE *fp, UINT w, UINT rows, int whence, BMP_SUBTYPE type); + +/*---------------------------------------------------------------------------*/ +/*-- Local Prototypes -------------------------------------------------------*/ + +static UINT getshort(FILE *fp); +static UINT getint(FILE *fp); +static void putshort(FILE *fp, int value); +static void putint(FILE *fp, int value); + +/*static int img_read_bmp_generic(const MYSTRING fname, int type, IMAGE **);*/ +#ifndef UNUSED_REDUCE_COLORS +static UCHAR *reduce_colors(UCHAR *buffin, int xsize, int ysize, + UCHAR *rmap, UCHAR *gmap, UCHAR *bmap, int nc); + +#endif + +#ifndef __LIBSIMAGE__ +#ifdef CICCIO + +static int loadBMP1(FILE *fp, LPIXEL *pic, UINT w, UINT h, UCHAR *r, UCHAR *g, UCHAR *b); +static int loadBMP4(FILE *fp, LPIXEL *pic, UINT w, UINT h, UINT comp, UCHAR *r, UCHAR *g, UCHAR *b); +static int loadBMP8(FILE *fp, LPIXEL *pic, UINT w, UINT h, UINT comp, UCHAR *r, UCHAR *g, UCHAR *b); +static int loadBMP24(FILE *fp, LPIXEL *pic, UINT w, UINT h); + +#endif +#endif /* __LIBSIMAGE__ */ + +static int writeBMP1(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *map); +static int writeBMP4(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *map); +static int writeBMPC4(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *map); +static int writeBMP8(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *map); +static int writeBMPC8(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *map); +static int writeBMP24(FILE *fp, UCHAR *pic24, UINT w, UINT h, UCHAR *map); + +static int load_lineBMP1(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map); +static int load_lineBMP4(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map); +static int load_lineBMPC4(FILE *fp, LPIXEL *pic, UINT w, UINT y, UCHAR **map); +static int load_lineBMP8(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map); +static int load_lineBMPC8(FILE *fp, LPIXEL *pic, UINT w, UINT y, UCHAR **map); +static int load_lineBMP24(FILE *fp, LPIXEL *pic, UINT w, UINT padb); + +static int line_writeBMP1(FILE *fp, UCHAR *pic8, UINT w, UINT padw, UCHAR *pc2nc); +static int line_writeBMP4(FILE *fp, UCHAR *pic8, UINT w, UINT padw, UCHAR *pc2nc); +static int line_writeBMPC4(FILE *fp, UCHAR *pic8, UINT w, UINT row, UCHAR *pc2nc); +static int line_writeBMP8(FILE *fp, UCHAR *pic8, UINT w, UINT padw, UCHAR *pc2nc); +static int line_writeBMPC8(FILE *fp, UCHAR *pic8, UINT w, UINT row, UCHAR *pc2nc); +static int line_writeBMP24(FILE *fp, LPIXEL *pp, UINT w, UINT padb); + +static int skip_rowsBMP1(FILE *fp, UINT w, UINT pad, UINT rows, int whence); +static int skip_rowsBMP4(FILE *fp, UINT w, UINT pad, UINT rows, int whence); +static int skip_rowsBMPC4(FILE *fp, UINT rows); +static int skip_rowsBMP8(FILE *fp, UINT w, UINT pad, UINT rows, int whence); +static int skip_rowsBMPC8(FILE *fp, UINT rows); +static int skip_rowsBMP24(FILE *fp, UINT w, UINT pad, UINT rows, int whence); + +#define BMP_DEBUG 0 +#define __BMP_WRITE_LINE_BY_LINE +#define __BMP_READ_LINE_BY_LINE + +/*---------------------------------------------------------------------------*/ +int read_bmp_line(FILE *fp, void *line_buffer, + UINT w, UINT row, UCHAR **map, BMP_SUBTYPE type) +/*---------------------------------------------------------------------------*/ +{ + LPIXEL *pic = (LPIXEL *)line_buffer; + unsigned int pad; + int rv = 0; + + switch (type) { + case BMP_BW: + pad = ((w + 31) / 32) * 32; + rv = load_lineBMP1(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY16 : __OR BMP_CMAPPED16 : pad = ((w + 7) / 8) * 8; + rv = load_lineBMP4(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY16C : __OR BMP_CMAPPED16C : rv = load_lineBMPC4(fp, pic, w, row, map); + if (rv == -1) + rv = 1; + else if (rv == -2) + rv = 0; + else if (rv == -3) + rv = 0; + else + rv = 0; + CASE BMP_GREY256 : __OR BMP_CMAPPED256 : pad = ((w + 3) / 4) * 4; + rv = load_lineBMP8(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY256C : __OR BMP_CMAPPED256C : rv = load_lineBMPC8(fp, pic, w, row, map); + if (rv == -1) + rv = 1; + else if (rv == -2) + rv = 0; + else if (rv == -3) + rv = 0; + else + rv = 0; + CASE BMP_RGB : pad = (4 - ((w * 3) % 4)) & 0x03; + rv = load_lineBMP24(fp, pic, w, (UINT)pad); + DEFAULT: + break; + } + + return !rv; /* return 0 for unsuccess */ +} + +/*---------------------------------------------------------------------------*/ +int write_bmp_line(FILE *fp, void *line_buffer, + UINT w, UINT row, UCHAR *map, BMP_SUBTYPE type) +/*---------------------------------------------------------------------------*/ +{ + UCHAR *pic = (UCHAR *)line_buffer; + LPIXEL *p24 = (LPIXEL *)line_buffer; + unsigned int pad; + int rv = 0; + + switch (type) { + case BMP_BW: + pad = ((w + 31) / 32) * 32; + rv = line_writeBMP1(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY16 : __OR BMP_CMAPPED16 : pad = ((w + 7) / 8) * 8; + rv = line_writeBMP4(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY16C : __OR BMP_CMAPPED16C : rv = line_writeBMPC4(fp, pic, w, row, map); + CASE BMP_GREY256 : __OR BMP_CMAPPED256 : pad = ((w + 3) / 4) * 4; + rv = line_writeBMP8(fp, pic, w, (UINT)pad, map); + CASE BMP_GREY256C : __OR BMP_CMAPPED256C : rv = line_writeBMPC8(fp, pic, w, row, map); + CASE BMP_RGB : pad = (4 - ((w * 3) % 4)) & 0x03; + rv = line_writeBMP24(fp, p24, w, (UINT)pad); + DEFAULT: + break; + } + + return rv; /* 0 for unsuccess */ +} + +/*---------------------------------------------------------------------------*/ +int skip_bmp_lines(FILE *fp, UINT w, UINT rows, int whence, BMP_SUBTYPE type) +/*---------------------------------------------------------------------------*/ +{ + unsigned int pad; + int rv = 0; + + switch (type) { + case BMP_BW: + pad = ((w + 31) / 32) * 32; + rv = skip_rowsBMP1(fp, w, (UINT)pad, rows, whence); + CASE BMP_GREY16 : __OR BMP_CMAPPED16 : pad = ((w + 7) / 8) * 8; + rv = skip_rowsBMP4(fp, w, (UINT)pad, rows, whence); + CASE BMP_GREY16C : __OR BMP_CMAPPED16C : rv = skip_rowsBMPC4(fp, rows); + CASE BMP_GREY256 : __OR BMP_CMAPPED256 : pad = ((w + 3) / 4) * 4; + rv = skip_rowsBMP8(fp, w, (UINT)pad, rows, whence); + CASE BMP_GREY256C : __OR BMP_CMAPPED256C : rv = skip_rowsBMPC8(fp, rows); + CASE BMP_RGB : pad = (4 - ((w * 3) % 4)) & 0x03; + rv = skip_rowsBMP24(fp, w, (UINT)pad, rows, whence); + DEFAULT: + break; + } + + return !rv; +} + +/*---------------------------------------------------------------------------*/ +int error_checking_bmp(BMP_HEADER *hd) +/*---------------------------------------------------------------------------*/ +{ + + /* error checking */ + if ((hd->biBitCount != 1 && + hd->biBitCount != 4 && + hd->biBitCount != 8 && + hd->biBitCount != 24) || + hd->biPlanes != 1 || hd->biCompression > BMP_BI_RLE4) { + return UNSUPPORTED_BMP_FORMAT; + } + + /* error checking */ + if (((hd->biBitCount == 1 || hd->biBitCount == 24) && + hd->biCompression != BMP_BI_RGB) || + (hd->biBitCount == 4 && hd->biCompression == BMP_BI_RLE8) || + (hd->biBitCount == 8 && hd->biCompression == BMP_BI_RLE4)) { + return UNSUPPORTED_BMP_FORMAT; + } + + return OK; +} + +/*---------------------------------------------------------------------------*/ +int load_bmp_header(FILE *fp, BMP_HEADER **header) +/*---------------------------------------------------------------------------*/ +{ + BMP_HEADER *hd = NULL; + int c, c1; + + *header = NULL; + + hd = (BMP_HEADER *)calloc((size_t)1, sizeof(BMP_HEADER)); + if (!hd) + return OUT_OF_MEMORY; + + /* figure out the file size */ + fseek(fp, 0L, SEEK_END); + hd->biFilesize = ftell(fp); + fseek(fp, 0L, 0); + + /* read the file type (first two bytes) */ + c = getc(fp); + c1 = getc(fp); + if (c != 'B' || c1 != 'M') { + free(hd); + return UNSUPPORTED_BMP_FORMAT; + } + + hd->bfSize = getint(fp); + + /* reserved and ignored */ + getshort(fp); + getshort(fp); + + hd->bfOffBits = getint(fp); + hd->biSize = getint(fp); + + if (hd->biSize == BMP_WIN_NEW || hd->biSize == BMP_OS2_NEW) { + hd->biWidth = getint(fp); + hd->biHeight = getint(fp); + hd->biPlanes = getshort(fp); + hd->biBitCount = getshort(fp); + hd->biCompression = getint(fp); + hd->biSizeImage = getint(fp); + hd->biXPelsPerMeter = getint(fp); + hd->biYPelsPerMeter = getint(fp); + hd->biClrUsed = getint(fp); + hd->biClrImportant = getint(fp); + } else /* old bitmap format */ + { + hd->biWidth = getshort(fp); + hd->biHeight = getshort(fp); + hd->biPlanes = getshort(fp); + hd->biBitCount = getshort(fp); + + /* Not in old versions so have to compute them */ + hd->biSizeImage = (((hd->biPlanes * + hd->biBitCount * + hd->biWidth) + + 31) / + 32) * + 4 * hd->biHeight; + + hd->biCompression = BMP_BI_RGB; + hd->biXPelsPerMeter = 0; + hd->biYPelsPerMeter = 0; + hd->biClrUsed = 0; + hd->biClrImportant = 0; + } + + if (BMP_DEBUG) { + printf("\nLoadBMP:\tbfSize=%u, bfOffBits=%u\n", hd->bfSize, + hd->bfOffBits); + printf("\t\tbiSize=%u, biWidth=%u, biHeight=%u, biPlanes=%u\n", + hd->biSize, + hd->biWidth, + hd->biHeight, + hd->biPlanes); + printf("\t\tbiBitCount=%u, biCompression=%u, biSizeImage=%u\n", + hd->biBitCount, + hd->biCompression, + hd->biSizeImage); + printf("\t\tbiX,YPelsPerMeter=%u,%u biClrUsed=%u, biClrImp=%u\n", + hd->biXPelsPerMeter, + hd->biYPelsPerMeter, + hd->biClrUsed, + hd->biClrImportant); + } + + if (BMP_FERROR(fp)) { + free(hd); + return UNEXPECTED_EOF; + } + + *header = hd; + return OK; +} + +/*---------------------------------------------------------------------------*/ +void release_bmp_header(BMP_HEADER *hd) +/*---------------------------------------------------------------------------*/ +{ + if (hd) + free(hd); +} + +#ifndef __LIBSIMAGE__ + +#ifdef CICCIO + +/*---------------------------------------------------------------------------*/ +int img_read_bmp(const MYSTRING fname, IMAGE **pimg) +/*---------------------------------------------------------------------------*/ +{ + return img_read_bmp_generic(fname, BMP_READ_IMAGE, pimg); +} + +/*---------------------------------------------------------------------------*/ +static int img_read_bmp_generic(const MYSTRING fname, int type, IMAGE **pimg) +/*---------------------------------------------------------------------------*/ +{ + int retCode = OK; + + UCHAR r[256], g[256], b[256]; + BMP_HEADER *hd = NULL; + IMAGE *img = NULL; + + int rv, c, i; + LPIXEL *pic; + FILE *fp; + + *pimg = 0; + + /* returns 'NULL' on unsuccess */ + + pic = (LPIXEL *)NULL; + + /* open image file */ + fp = _wfopen(fname, L"rb"); + if (!fp) + return CANT_OPEN_FILE; + + /* load up the image header */ + retCode = load_bmp_header(fp, &hd); + if (retCode != OK) + goto ERROR; + + /* error checking */ + if ((hd->biBitCount != 1 && + hd->biBitCount != 4 && + hd->biBitCount != 8 && + hd->biBitCount != 24) || + hd->biPlanes != 1 || hd->biCompression > BMP_BI_RLE4) { + retCode = UNSUPPORTED_BMP_FORMAT; + goto ERROR; + } + + /* error checking */ + if (((hd->biBitCount == 1 || hd->biBitCount == 24) && + hd->biCompression != BMP_BI_RGB) || + (hd->biBitCount == 4 && hd->biCompression == BMP_BI_RLE8) || + (hd->biBitCount == 8 && hd->biCompression == BMP_BI_RLE4)) { + retCode = UNSUPPORTED_BMP_FORMAT; + goto ERROR; + } + + img = new_img(); + img->type = TOONZRGB; + + if (type == BMP_READ_INFO) { + fclose(fp); + img->xSBsize = img->xsize = hd->biWidth; + img->ySBsize = img->ysize = hd->biHeight; + release_bmp_header(hd); + *pimg = img; + return OK; + } + + allocate_pixmap(img, (int)hd->biWidth, (int)hd->biHeight); + + /* hd->biPad; */ + if (hd->biSize != BMP_WIN_OS2_OLD) { + /* skip ahead to colormap, using biSize */ + c = hd->biSize - 40; /* 40 bytes read from biSize to biClrImportant */ + for (i = 0; i < c; i++) + getc(fp); + hd->biPad = hd->bfOffBits - (hd->biSize + 14); + } + + /* load up colormap, if any */ + if (hd->biBitCount != 24) { + int i, cmaplen; + + /*cmaplen = (hd->biClrUsed) ? hd->biClrUsed : 1 << hd->biBitCount;*/ + if (hd->biClrUsed) + cmaplen = hd->biClrUsed; + else + cmaplen = 1 << hd->biBitCount; + + for (i = 0; i < cmaplen; i++) { + b[i] = getc(fp); + g[i] = getc(fp); + r[i] = getc(fp); + if (hd->biSize != BMP_WIN_OS2_OLD) { + getc(fp); + hd->biPad -= 4; + } + } + + if (BMP_FERROR(fp)) { + retCode = UNEXPECTED_EOF; + goto ERROR; + } + + if (BMP_DEBUG) { + printf("LoadBMP: BMP colormap: (RGB order)\n"); + for (i = 0; i < cmaplen; i++) + printf("%02x%02x%02x ", r[i], g[i], b[i]); + printf("\n\n"); + } + } + + if (hd->biSize != BMP_WIN_OS2_OLD) { + /* Waste any unused bytes between the colour map (if present) + and the start of the actual bitmap data. + */ + + while (hd->biPad > 0) { + (void)getc(fp); + hd->biPad--; + } + } + + /* create 32 bit image buffer */ + + pic = (LPIXEL *)img->buffer; + + /* load up the image */ + switch (hd->biBitCount) { + case 1: + rv = loadBMP1(fp, pic, hd->biWidth, hd->biHeight, r, g, b); + CASE 4 : rv = loadBMP4(fp, pic, hd->biWidth, hd->biHeight, hd->biCompression, r, g, b); + CASE 8 : rv = loadBMP8(fp, pic, hd->biWidth, hd->biHeight, hd->biCompression, r, g, b); + CASE 24 : rv = loadBMP24(fp, pic, hd->biWidth, hd->biHeight); + DEFAULT: + retCode = UNSUPPORTED_BMP_FORMAT; + goto ERROR; + } + + if (rv) { + retCode = UNEXPECTED_EOF; + goto ERROR; + } + + fclose(fp); + release_bmp_header(hd); + + *pimg = img; + return OK; + +ERROR: + + fclose(fp); + release_bmp_header(hd); + + if (img) { + TFREE(img->buffer) + TFREE(img) + } + + return retCode; +} + +#endif + +#endif /* __LIBSIMAGE__ */ + +#ifdef CICCIO +static int img_read_bmp_region(const MYSTRING fname, IMAGE **pimg, int x1, int y1, int x2, int y2, int scale) +{ + UCHAR r[256], g[256], b[256] /* ,*map[3] */; + LPIXEL *line = NULL; + UINT line_size = 0; + BMP_HEADER *hd = NULL; + EXT_INFO_REGION info; + BMP_SUBTYPE subtype; + LPIXEL *pic, *appo; + IMAGE *img = NULL; + UINT start_offset; + UINT c, i, j; + char buf[512]; + FILE *fp; + UINT pad; + enum BMP_ERROR_CODE bmp_error = OK; + + /* initialize some variables */ + i = pad = 0; + + /* returns 'NULL' on unsuccess */ + pic = (LPIXEL *)NULL; + + /* open image file */ + fp = _wfopen(fname, L"rb"); + if (!fp) { + return CANT_OPEN_FILE; + } + + /* load up the image header */ + load_bmp_header(fp, &hd); + if (!hd) + goto ERROR; + + /* error checking */ + if ((hd->biBitCount != 1 && + hd->biBitCount != 4 && + hd->biBitCount != 8 && + hd->biBitCount != 24) || + hd->biPlanes != 1 || hd->biCompression > BMP_BI_RLE4) { + sprintf(buf, "Bogus BMP File! (bitCount=%d, Planes=%d, Compression=%d)", + hd->biBitCount, hd->biPlanes, hd->biCompression); + + bmp_error = UNSUPPORTED_BMP_FORMAT; + goto ERROR; + } + + /* error checking */ + if (((hd->biBitCount == 1 || hd->biBitCount == 24) && + hd->biCompression != BMP_BI_RGB) || + (hd->biBitCount == 4 && hd->biCompression == BMP_BI_RLE8) || + (hd->biBitCount == 8 && hd->biCompression == BMP_BI_RLE4)) { + sprintf(buf, "Bogus BMP File! (bitCount=%d, Compression=%d)", + hd->biBitCount, hd->biCompression); + bmp_error = UNSUPPORTED_BMP_FORMAT; + goto ERROR; + } + + img = new_img(); + + img->type = TOONZRGB; + + img->xsize = hd->biWidth; + img->ysize = hd->biHeight; + img->xSBsize = hd->biWidth; + img->ySBsize = hd->biHeight; + img->x_dpi = (double)(hd->biXPelsPerMeter / 39); + img->y_dpi = (double)(hd->biYPelsPerMeter / 39); + + hd->biPad = 0; + if (hd->biSize != BMP_WIN_OS2_OLD) { + /* skip ahead to colormap, using biSize */ + c = hd->biSize - 40; /* 40 bytes read from biSize to biClrImportant */ + for (i = 0; i < c; i++) + getc(fp); + hd->biPad = hd->bfOffBits - (hd->biSize + 14); + } + + /* load up colormap, if any */ + if (hd->biBitCount != 24) { + int i, cmaplen; + + /*cmaplen = (hd->biClrUsed) ? hd->biClrUsed : 1 << hd->biBitCount;*/ + if (hd->biClrUsed) + cmaplen = hd->biClrUsed; + else + cmaplen = hd->biBitCount; + + for (i = 0; i < cmaplen; i++) { + b[i] = getc(fp); + g[i] = getc(fp); + r[i] = getc(fp); + if (hd->biSize != BMP_WIN_OS2_OLD) { + getc(fp); + hd->biPad -= 4; + } + } + + if (BMP_FERROR(fp)) { + bmp_error = UNEXPECTED_EOF; + goto ERROR; + } + + if (BMP_DEBUG) { + printf("LoadBMP: BMP colormap: (RGB order)\n"); + for (i = 0; i < cmaplen; i++) + printf("%02x%02x%02x ", r[i], g[i], b[i]); + printf("\n\n"); + } + } + + if (hd->biSize != BMP_WIN_OS2_OLD) { + /* Waste any unused bytes between the colour map (if present) + and the start of the actual bitmap data. + */ + + while (hd->biPad > 0) { + (void)getc(fp); + hd->biPad--; + } + } + + /* get information about the portion of the image to load */ + get_info_region(&info, x1, y1, x2, y2, scale, + (int)hd->biWidth, (int)hd->biHeight, TNZ_BOTLEFT); + + /* create 32 bit image buffer */ + if (!allocate_pixmap(img, info.xsize, info.ysize)) { + bmp_error = OUT_OF_MEMORY; + goto ERROR; + } + + start_offset = info.y_offset * info.xsize + info.x_offset; + pic = ((LPIXEL *)img->buffer) + start_offset; + + if (line_size < hd->biWidth + 32) { + line_size = hd->biWidth + 32; + if (!line) + TCALLOC((LPIXEL *)line, (size_t)line_size) + else + TREALLOC(line, line_size) + } + if (!line) { + bmp_error = OUT_OF_MEMORY; + goto ERROR; + } + + switch (hd->biBitCount) { + case 1: + pad = ((hd->biWidth + 31) / 32) * 32; + CASE 4 : pad = ((hd->biWidth + 7) / 8) * 8; + CASE 8 : pad = ((hd->biWidth + 3) / 4) * 4; + CASE 24 : pad = (4 - ((hd->biWidth * 3) % 4)) & 0x03; + DEFAULT: + /* segnala errore ed esci */ + break; + } + + subtype = bmp_get_colorstyle(img); + if (subtype == BMP_NONE) + goto ERROR; + + if (info.y_offset > 0) + info.scanNrow++; + if (info.x_offset > 0) + info.scanNcol++; + + /* print_info_region(&info); */ + + if (info.startScanRow > 0) + skip_bmp_lines(fp, hd->biWidth, (unsigned int)(info.startScanRow - 1), (unsigned int)SEEK_CUR, subtype); + + for (i = 0; i < (UINT)info.scanNrow; i++) { + if (load_lineBMP24(fp, line, hd->biWidth, pad)) + goto ERROR; + + /* QUESTO SWITCH VA AGGIUSTATO! + switch (subtype) + { + CASE BMP_BW: + if (load_lineBMP1(fp, line, hd->biWidth, pad, map)) + goto ERROR; + CASE BMP_GREY16: + __OR BMP_CMAPPED16: + if (load_lineBMP4(fp, line, hd->biWidth, pad, map)) + goto ERROR; + CASE BMP_GREY16C: + __OR BMP_CMAPPED16C: + if (load_lineBMPC4(fp, line, hd->biWidth, i, map)==-1) + goto ERROR; + CASE BMP_GREY256: + __OR BMP_CMAPPED256: + if (load_lineBMP8(fp, line, hd->biWidth, pad, map)) + goto ERROR; + CASE BMP_GREY256C: + __OR BMP_CMAPPED256C: + if (load_lineBMPC8(fp, line, hd->biWidth, i, map)==-1) + goto ERROR; + CASE BMP_RGB: + if (load_lineBMP24(fp, line, hd->biWidth, pad)) + goto ERROR; +} +*/ + for (appo = pic, j = c = 0; j < (UINT)info.scanNcol; j++, c += info.step) + *appo++ = *(line + info.startScanCol + c); + pic += info.xsize; + + skip_bmp_lines(fp, hd->biWidth, (unsigned int)(info.step - 1), (unsigned int)SEEK_CUR, subtype); + } + + /* + if (BMP_FERROR(fp)) + { + bmp_error(fname, "File appears truncated. Winging it.\n"); + goto ERROR; + } +*/ + + fclose(fp); + release_bmp_header(hd); + TFREE(line); + *pimg = img; + return OK; + +ERROR: + + printf("error: (row=%d)\n", i); + + fclose(fp); + release_bmp_header(hd); + + if (img) + free_img(img); + TFREE(line); + return bmp_error; +} + +#endif + +/*---------------------------------------------------------------------------*/ + +#ifndef __LIBSIMAGE__ +#ifdef CICCIO + +static int loadBMP1(FILE *fp, LPIXEL *pic, UINT w, UINT h, UCHAR *r, UCHAR *g, UCHAR *b) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, c, bitnum, padw, rv; + UCHAR byte; + LPIXEL *pp; +#ifdef BMP_READ_LINE_BY_LINE + UCHAR *map[3]; + + map[0] = r; + map[1] = g; + map[2] = b; +#endif + + rv = c = 0; + padw = ((w + 31) / 32) * 32; /* 'w', padded to be a multiple of 32 */ + + for (i = 0; i < h; i++) { +#ifdef BMP_READ_LINE_BY_LINE + pp = pic + (i * w); + rv = load_lineBMP1(fp, pp, w, padw, map); + if (rv) + break; +#else + pp = pic + (i * w); + for (j = bitnum = 0; j < padw; j++, bitnum++) { + if ((bitnum & 7) == 0) /* read the next byte */ + { + c = getc(fp); + bitnum = 0; + } + if (j < w) { + byte = (c & 0x80) ? 1 : 0; + c <<= 1; + + pp->r = r[byte]; + pp->g = g[byte]; + pp->b = b[byte]; + pp->m = 255; + + pp++; + } + } + if (BMP_FERROR(fp)) + break; +#endif + } + + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +#endif +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +int load_lineBMP1(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map) +/*---------------------------------------------------------------------------*/ +{ + UINT j, c, bitnum; + UCHAR byte; + LPIXEL *pp; + + for (c = 0, pp = pic, j = bitnum = 0; j < padw; j++, bitnum++) { + if ((bitnum & 7) == 0) /* read the next byte */ + { + c = getc(fp); + bitnum = 0; + } + if (j < w) { + byte = (c & 0x80) ? 1 : 0; + c <<= 1; + + pp->r = map[0][byte]; + pp->g = map[1][byte]; + pp->b = map[2][byte]; + pp->m = 255; + + pp++; + } + } + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMP1(FILE *fp, UINT w, UINT pad, UINT rows, int whence) +/*---------------------------------------------------------------------------*/ +{ + UINT offset = pad * rows; + UINT i, bitnum; + + for (i = bitnum = 0; i < offset; i++, bitnum++) { + if ((bitnum & 7) == 0) { + getc(fp); + bitnum = 0; + } + } + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ + +#ifndef __LIBSIMAGE__ +#ifdef CICCIO + +static int loadBMP4(FILE *fp, + LPIXEL *pic, UINT w, UINT h, UINT comp, UCHAR *r, UCHAR *g, UCHAR *b) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, c, c1, x, y, nybnum, padw, rv; + UCHAR byte; + LPIXEL *pp; +#ifdef BMP_READ_LINE_BY_LINE + UCHAR *map[3]; + + map[0] = r; + map[1] = g; + map[2] = b; +#endif + + rv = 0; + c = c1 = 0; + + if (comp == BMP_BI_RGB) /* read uncompressed data */ + { + padw = ((w + 7) / 8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */ + for (i = 0; i < h; i++) { + pp = pic + (i * w); +#ifdef BMP_READ_LINE_BY_LINE + rv = load_lineBMP4(fp, pp, w, padw, map); + if (rv) + break; +#else + for (j = nybnum = 0; j < padw; j++, nybnum++) { + if ((nybnum & 1) == 0) /* read next byte */ + { + c = getc(fp); + nybnum = 0; + } + if (j < w) { + byte = (c & 0xf0) >> 4; + c <<= 4; + + pp->r = r[byte]; + pp->g = g[byte]; + pp->b = b[byte]; + pp->m = 255; + + pp++; + } + } + if (BMP_FERROR(fp)) + break; +#endif + } + } else if (comp == BMP_BI_RLE4) /* read RLE4 compressed data */ + { + x = y = 0; + pp = pic + x + (y)*w; + while (y < h) { +#ifdef BMP_READ_LINE_BY_LINE + + rv = load_lineBMPC4(fp, pp, w, y, map); + if (rv == -1) { + rv = 1; + break; + } else if (rv == -2) { + rv = 0; + y++; + pp = pic + y * w; + } else if (rv == -3) { + rv = 0; + break; + } else { + y += (rv / w); + pp = pic + rv; + rv = 0; + } + +#else + c = getc(fp); + if ((int)c == EOF) { + rv = 1; + break; + } + + if (c) /* encoded mode */ + { + c1 = getc(fp); + for (i = 0; i < c; i++, x++, pp++) { + byte = (i & 1) ? (c1 & 0x0f) : ((c1 >> 4) & 0x0f); + pp->r = r[byte]; + pp->g = g[byte]; + pp->b = b[byte]; + pp->m = 255; + } + } else /* c==0x00 : escape codes */ + { + c = getc(fp); + if ((int)c == EOF) { + rv = 1; + break; + } + + if (c == 0x00) /* end of line */ + { + x = 0; + y++; + if (y < h) + pp = pic + x + (y)*w; + } else if (c == 0x01) + break; /* end of pic */ + else if (c == 0x02) /* delta */ + { + c = getc(fp); + x += c; + c = getc(fp); + y += c; + if (y < h) + pp = pic + x + (y)*w; + } else /* absolute mode */ + { + for (i = 0; i < c; i++, x++, pp++) { + if ((i & 1) == 0) + c1 = getc(fp); + byte = (i & 1) ? (c1 & 0x0f) : ((c1 >> 4) & 0x0f); + pp->r = r[byte]; + pp->g = g[byte]; + pp->b = b[byte]; + pp->m = 255; + } + if (((c & 3) == 1) || ((c & 3) == 2)) /* read pad byte */ + getc(fp); + } + } + if (BMP_FERROR(fp)) + break; +#endif + } + } else { + return 1; + } + + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +#endif +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +int load_lineBMP4(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map) +/*---------------------------------------------------------------------------*/ +{ + UINT nybnum, j, c; + UCHAR byte; + LPIXEL *pp; + + for (c = 0, pp = pic, j = nybnum = 0; j < padw; j++, nybnum++) { + if ((nybnum & 1) == 0) /* read next byte */ + { + c = getc(fp); + nybnum = 0; + } + if (j < w) { + byte = (c & 0xf0) >> 4; + c <<= 4; + + pp->r = map[0][byte]; + pp->g = map[1][byte]; + pp->b = map[2][byte]; + pp->m = 255; + + pp++; + } + } + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMP4(FILE *fp, UINT w, UINT pad, UINT rows, int whence) +/*---------------------------------------------------------------------------*/ +{ + UINT offset = pad * rows; + UINT i, nybnum; + + for (i = nybnum = 0; i < offset; i++, nybnum++) { + if ((nybnum & 1) == 0) { + getc(fp); + nybnum = 0; + } + } + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +int load_lineBMPC4(FILE *fp, LPIXEL *pic, UINT w, UINT y, UCHAR **map) +/*---------------------------------------------------------------------------*/ +{ + UINT i, c, c1, x; + UCHAR byte; + LPIXEL *pp; + + /* + * Codici di ritorno: + * + * -1: incontrata la file del file (EOF) + * -2: incontrata la fine della linea (Escape code 0x00 0x00) + * -3: incontrata la fine dell' immagine (Escape code 0x00 0x01) + * altro: incontrato un delta (Escape code 0x00 0x02) + */ + + /* initialize some variables */ + x = 0; + pp = pic; + c = c1 = 0; + + while (1) { + c = getc(fp); + if ((int)c == EOF) + return -1; + if (c) { /* encoded mode */ + c1 = getc(fp); + for (i = 0; i < c; i++, x++, pp++) { + byte = (i & 1) ? (c1 & 0x0f) : ((c1 >> 4) & 0x0f); + pp->r = map[0][byte]; + pp->g = map[1][byte]; + pp->b = map[2][byte]; + pp->m = 255; + } + } else /* c==0x00 : escape codes */ + { + c = getc(fp); + if ((int)c == EOF) + return -1; + if (c == 0x00) /* end of line */ + return -2; + else if (c == 0x01) /* end of pic */ + return -3; + else if (c == 0x02) /* delta */ + { + c = getc(fp); + x += c; + c = getc(fp); + y += c; + + return (x + y * w); + } else /* absolute mode */ + { + for (i = 0; i < c; i++, x++, pp++) { + if ((i & 1) == 0) + c1 = getc(fp); + byte = (i & 1) ? (c1 & 0x0f) : ((c1 >> 4) & 0x0f); + pp->r = map[0][byte]; + pp->g = map[1][byte]; + pp->b = map[2][byte]; + pp->m = 255; + } + if (((c & 3) == 1) || ((c & 3) == 2)) /* read pad byte */ + getc(fp); + } + } + if (BMP_FERROR(fp)) + break; + } + + return -1; +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMPC4(FILE *fp, UINT rows) +/*---------------------------------------------------------------------------*/ +{ + UINT i, c, c1, rv = 0; + + while (rows > 0) { + c = getc(fp); + switch (c) { + case 0x00: + c = getc(fp); + switch (c) { + case 0x00: + rows--; + CASE 0x01 : rows = 0; + CASE 0x02 : c1 = getc(fp); /* x buffer offest */ + c1 = getc(fp); /* y buffer offest */ + rows -= c1; + DEFAULT: + for (i = 0; i < c; i++) { + if ((i & 1) == 0) + getc(fp); + } + if (((c & 3) == 1) || ((c & 3) == 2)) + getc(fp); + } + DEFAULT: + c1 = getc(fp); + } + } + + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +/*---------------------------------------------------------------------------*/ + +#ifndef __LIBSIMAGE__ +#ifdef CICCIO + +static int loadBMP8(FILE *fp, + LPIXEL *pic, UINT w, UINT h, UINT comp, UCHAR *r, UCHAR *g, UCHAR *b) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, c, c1, padw, x, y, rv; + + LPIXEL *pp; +#ifdef BMP_READ_LINE_BY_LINE + UCHAR *map[3]; + + map[0] = r; + map[1] = g; + map[2] = b; +#endif + + rv = 0; + + if (comp == BMP_BI_RGB) /* read uncompressed data */ + { + padw = ((w + 3) / 4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */ + for (i = 0; i < h; i++) { +#ifdef BMP_READ_LINE_BY_LINE + pp = pic + (i * w); + rv = load_lineBMP8(fp, pp, w, padw, map); + if (rv) + break; +#else + pp = pic + (i * w); + for (j = 0; j < padw; j++) { + c = getc(fp); + if ((int)c == EOF) + rv = 1; + if (j < w) { + pp->r = r[c]; + pp->g = g[c]; + pp->b = b[c]; + pp->m = 255; + + pp++; + } + } + if (BMP_FERROR(fp)) + break; +#endif + } + } else if (comp == BMP_BI_RLE8) /* read RLE8 compressed data */ + { + x = y = 0; + pp = pic + x + y * w; + while (y < h) { +#ifdef BMP_READ_LINE_BY_LINE + + rv = load_lineBMPC8(fp, pp, w, y, map); + if (rv == -1) { + rv = 1; + break; + } else if (rv == -2) { + rv = 0; + y++; + pp = pic + y * w; + } else if (rv == -3) { + rv = 0; + break; + } else { + y += (rv / w); + pp = pic + rv; + rv = 0; + } + +#else + c = getc(fp); + if ((int)c == EOF) { + rv = 1; + break; + } + + if (c) { /* encoded mode */ + c1 = getc(fp); + for (i = 0; i < c; i++, x++, pp++) { + pp->r = r[c1]; + pp->g = g[c1]; + pp->b = b[c1]; + pp->m = 255; + } + } else /* c==0x00 : escape codes */ + { + c = getc(fp); + if ((int)c == EOF) { + rv = 1; + break; + } + + if (c == 0x00) /* end of line */ + { + x = 0; + y++; + pp = pic + x + y * w; + } else if (c == 0x01) + break; /* end of pic */ + else if (c == 0x02) /* delta */ + { + c = getc(fp); + x += c; + c = getc(fp); + y += c; + pp = pic + x + y * w; + } else /* absolute mode */ + { + for (i = 0; i < c; i++, x++, pp++) { + c1 = getc(fp); + + pp->r = r[c1]; + pp->g = g[c1]; + pp->b = b[c1]; + pp->m = 255; + } + if (c & 1) /* odd length run: read an extra pad byte */ + getc(fp); + } + } + if (BMP_FERROR(fp)) + break; +#endif + } + } else { + return 1; + } + + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +#endif +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +int load_lineBMP8(FILE *fp, LPIXEL *pic, UINT w, UINT padw, UCHAR **map) +/*---------------------------------------------------------------------------*/ +{ + UINT j, c, rv = 0; + LPIXEL *pp; + + for (pp = pic, j = 0; j < padw; j++) { + c = getc(fp); + if ((int)c == EOF) { + rv = 1; + break; + } + if (j < w) { + pp->r = map[0][c]; + pp->g = map[1][c]; + pp->b = map[2][c]; + pp->m = 255; + + pp++; + } + } + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMP8(FILE *fp, UINT w, UINT pad, UINT rows, int whence) +/*---------------------------------------------------------------------------*/ +{ + UINT offset = pad * rows; + + fseek(fp, (long)offset, whence); + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +int load_lineBMPC8(FILE *fp, LPIXEL *pic, UINT w, UINT y, UCHAR **map) +/*---------------------------------------------------------------------------*/ +{ + int i, c, c1, x; + LPIXEL *pp; + + /* + * Codici di ritorno: + * + * -1: incontrata la file del file (EOF) + * -2: incontrata la fine della linea (Escape code 0x00 0x00) + * -3: incontrata la fine dell' immagine (Escape code 0x00 0x01) + * altro: incontrato un delta (Escape code 0x00 0x02) + */ + + x = 0; + pp = pic; + + while (1) { + c = getc(fp); + if (c == EOF) + return -1; + if (c) { /* encoded mode */ + c1 = getc(fp); + for (i = 0; i < c; i++, x++, pp++) { + pp->r = map[0][c1]; + pp->g = map[1][c1]; + pp->b = map[2][c1]; + pp->m = 255; + } + } else /* c==0x00 : escape codes */ + { + c = getc(fp); + if (c == EOF) + return -1; + if (c == 0x00) /* end of line */ + return -2; + else if (c == 0x01) /* end of pic */ + return -3; + else if (c == 0x02) /* delta */ + { + c = getc(fp); + x += c; + c = getc(fp); + y += c; + + return (x + y * w); + } else /* absolute mode */ + { + for (i = 0; i < c; i++, x++, pp++) { + c1 = getc(fp); + + pp->r = map[0][c1]; + pp->g = map[1][c1]; + pp->b = map[2][c1]; + pp->m = 255; + } + if (c & 1) /* odd length run: read an extra pad byte */ + getc(fp); + } + } + } +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMPC8(FILE *fp, UINT rows) +/*---------------------------------------------------------------------------*/ +{ + int i, c, c1, rv = 0; + + while (rows > 0) { + c = getc(fp); + switch (c) { + case 0x00: + c = getc(fp); + switch (c) { + case 0x00: + rows--; + CASE 0x01 : rows = 0; + CASE 0x02 : c1 = getc(fp); /* x buffer offest */ + c1 = getc(fp); /* y buffer offest */ + rows -= c1; + DEFAULT: + for (i = 0; i < c; i++) + getc(fp); + if (c & 1) + getc(fp); + } + DEFAULT: + c1 = getc(fp); + } + } + + if (BMP_FERROR(fp)) + rv = 1; + + return rv; +} + +/*---------------------------------------------------------------------------*/ + +#ifndef __LIBSIMAGE__ +#ifdef CICCIO + +static int loadBMP24(FILE *fp, LPIXEL *pic, UINT w, UINT h) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, padb, rv; + LPIXEL *pp; + + rv = 0; + + padb = (4 - ((w * 3) % 4)) & 0x03; /* # of pad bytes to read at EOscanline */ + + for (i = 0; i < h; i++) { +#ifdef BMP_READ_LINE_BY_LINE + pp = pic + i * w; + rv = load_lineBMP24(fp, pp, w, padb); +#else + for (pp = pic + i * w, j = 0; j < w; j++, pp++) { + pp->b = getc(fp); /* blue */ + pp->g = getc(fp); /* green */ + pp->r = getc(fp); /* red */ + pp->m = 255; + } + for (j = 0; j < padb; j++) + getc(fp); + + rv = (BMP_FERROR(fp)); +#endif + if (rv) + break; + } + + return rv; +} + +#endif +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +int load_lineBMP24(FILE *fp, LPIXEL *pic, UINT w, UINT padb) +/*---------------------------------------------------------------------------*/ +{ + LPIXEL *pp; + UINT j; + + for (pp = pic, j = 0; j < w; j++, pp++) { + pp->b = getc(fp); /* blue */ + pp->g = getc(fp); /* green */ + pp->r = getc(fp); /* red */ + pp->m = 255; + } + for (j = 0; j < padb; j++) + getc(fp); + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +static int skip_rowsBMP24(FILE *fp, UINT w, UINT pad, UINT rows, int whence) +/*---------------------------------------------------------------------------*/ +{ + UINT offset = (w * 3 + pad) * rows; + + fseek(fp, (long)offset, whence); + + return (BMP_FERROR(fp)); +} + +/*---------------------------------------------------------------------------*/ +/*-- BMP WRITE --------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +BMP_SUBTYPE bmp_get_colorstyle(IMAGE *img) +/*---------------------------------------------------------------------------*/ +{ + return img->type; +} + +/*---------------------------------------------------------------------------*/ +int write_bmp_header(FILE *fp, BMP_HEADER *hd) +/*---------------------------------------------------------------------------*/ +{ + putc('B', fp); + putc('M', fp); /* BMP file magic number */ + + putint(fp, (int)hd->bfSize); + putshort(fp, 0); /* reserved1 */ + putshort(fp, 0); /* reserved2 */ + + putint(fp, (int)hd->bfOffBits); /* offset from BOfile to BObitmap */ + + putint(fp, (int)hd->biSize); /* size of bitmap info header */ + putint(fp, (int)hd->biWidth); /* width */ + putint(fp, (int)hd->biHeight); /* height */ + putshort(fp, (int)hd->biPlanes); /* must be '1' */ + putshort(fp, (int)hd->biBitCount); /* 1,4,8, or 24 */ + putint(fp, (int)hd->biCompression); /* BMP_BI_RGB, BMP_BI_RLE8 or BMP_BI_RLE4 */ + putint(fp, (int)hd->biSizeImage); /* size of raw image data */ + putint(fp, (int)hd->biXPelsPerMeter); /* dpi * 39" per meter */ + putint(fp, (int)hd->biYPelsPerMeter); /* dpi * 39" per meter */ + putint(fp, (int)hd->biClrUsed); /* colors used in cmap */ + putint(fp, (int)hd->biClrImportant); /* same as above */ + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +int write_bmp_palette(FILE *fp, int nc, UCHAR *b, UCHAR *g, UCHAR *r) +/*---------------------------------------------------------------------------*/ +{ + int i; + + for (i = 0; i < nc; i++) { + putc(b[i], fp); + putc(g[i], fp); + putc(r[i], fp); + putc(0, fp); + } + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +#ifndef __LIBSIMAGE__ + +/*---------------------------------------------------------------------------*/ +int img_write_bmp(const MYSTRING fname, IMAGE *img) +/*---------------------------------------------------------------------------*/ +{ + int (*write_function)(FILE *fp, UCHAR *pic, UINT w, UINT h, UCHAR *map); + int h, w, i, nc, nbits, bperlin, comp; + UCHAR val; + UCHAR pc2nc[256], r1[256], g1[256], b1[256]; + UCHAR *pic, *graypic; + BMP_SUBTYPE subtype; + BMP_HEADER hd; + FILE *fp; + + subtype = bmp_get_colorstyle(img); + if (subtype == BMP_NONE) + return UNSUPPORTED_BMP_FORMAT; + + fp = _wfopen(fname, L"wb"); + if (!fp) + return CANT_OPEN_FILE; + + graypic = NULL; + nc = 0; + nbits = 0; + comp = 0; + h = img->ysize; + w = img->xsize; + pic = (UCHAR *)img->buffer; + + switch (subtype) { + case BMP_BW: + __OR BMP_GREY16 : __OR BMP_GREY16C : + + __OR BMP_CMAPPED256 : __OR BMP_CMAPPED256C : return UNSUPPORTED_BMP_FORMAT; + CASE BMP_GREY256 : __OR BMP_GREY256C : nbits = 8; + CASE BMP_RGB : nbits = 24; + DEFAULT: + goto BMP_WRITE_ERROR; + } + + /* number bytes written per line */ + bperlin = ((w * nbits + 31) / 32) * 4; + + /* compute filesize and write it */ + i = 14 + /* size of bitmap file header */ + 40 + /* size of bitmap info header */ + (nc * 4) + /* size of colormap */ + bperlin * h; /* size of image data */ + + switch (nbits) { + case 4: + comp = (comp == TRUE) ? BMP_BI_RLE4 : BMP_BI_RGB; + CASE 8 : comp = (comp == TRUE) ? BMP_BI_RLE8 : BMP_BI_RGB; + DEFAULT: + comp = BMP_BI_RGB; + } + + /* fill image header */ + hd.bfSize = i; + hd.bfOffBits = 14 + 40 + (nc * 4); + hd.biSize = 40; + hd.biWidth = w; + hd.biHeight = h; + hd.biPlanes = 1; + hd.biBitCount = nbits; + hd.biCompression = comp; + hd.biSizeImage = bperlin * h; + hd.biXPelsPerMeter = 0 * 39; + hd.biYPelsPerMeter = 0 * 39; + hd.biClrUsed = nc; + hd.biClrImportant = nc; + + if (!write_bmp_header(fp, &hd)) + goto BMP_WRITE_ERROR; + + write_bmp_palette(fp, nc, b1, g1, r1); + + switch (nbits) { + case 1: + write_function = writeBMP1; + CASE 4 : if (comp == BMP_BI_RGB) write_function = writeBMP4; + else write_function = writeBMPC4; + CASE 8 : if (comp == BMP_BI_RGB) write_function = writeBMP8; + else write_function = writeBMPC8; + CASE 24 : write_function = writeBMP24; + DEFAULT: + goto BMP_WRITE_ERROR; + } + + /* write out the image */ + val = write_function(fp, pic, w, h, pc2nc); + + if (graypic) + free(graypic); + fclose(fp); + + /* 0 failed , 1 ok */ + return val == 1 ? OK : WRITE_ERROR; + +BMP_WRITE_ERROR: + + fclose(fp); + if (graypic) + free(graypic); + + _wremove(fname); + + return WRITE_ERROR; +} + +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +static int writeBMP1(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, c, bitnum, padw; + UCHAR *pp; + + padw = ((w + 31) / 32) * 32; /* 'w', padded to be a multiple of 32 */ + + for (i = 0; i < h; i++) { + pp = pic8 + (i * w); +#ifdef BMP_WRITE_LINE_BY_LINE + if (line_writeBMP1(fp, pp, w, padw, pc2nc) == FALSE) + return FALSE; +#else + for (j = bitnum = c = 0; j <= padw; j++, bitnum++) { + if (bitnum == 8) /* write the next byte */ + { + putc((int)c, fp); + bitnum = c = 0; + } + c <<= 1; + if (j < w) { + c |= (pc2nc[*pp++] & 0x01); + } + } +#endif + } + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int line_writeBMP1(FILE *fp, UCHAR *pic8, UINT w, UINT padw, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UCHAR *pp = pic8; + UINT j, c, bitnum; + + for (j = bitnum = c = 0; j <= padw; j++, bitnum++) { + if (bitnum == 8) /* write the next byte */ + { + putc((int)c, fp); + bitnum = c = 0; + } + c <<= 1; + if (j < w) { + c |= (pc2nc[*pp++] & 0x01); + } + } + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int writeBMP4(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, c, nybnum, padw; + UCHAR *pp; + + padw = ((w + 7) / 8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */ + + for (i = 0; i < h; i++) { + pp = pic8 + (i * w); +#ifdef BMP_WRITE_LINE_BY_LINE + if (line_writeBMP4(fp, pp, w, padw, pc2nc) == FALSE) + return FALSE; +#else + for (j = nybnum = c = 0; j <= padw; j++, nybnum++) { + if (nybnum == 2) /* write next byte */ + { + putc((int)(c & 0xff), fp); + nybnum = c = 0; + } + c <<= 4; + if (j < w) { + c |= (pc2nc[*pp] & 0x0f); + pp++; + } + } +#endif + } + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int line_writeBMP4(FILE *fp, UCHAR *pic8, UINT w, UINT padw, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UINT j, c, nybnum; + UCHAR *pp = pic8; + + for (j = nybnum = c = 0; j <= padw; j++, nybnum++) { + if (nybnum == 2) /* write next byte */ + { + putc((int)(c & 0xff), fp); + nybnum = c = 0; + } + c <<= 4; + if (j < w) { + c |= (pc2nc[*pp] & 0x0f); + pp++; + } + } + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int writeBMPC4(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UCHAR *pp1, *pp2, *pp3, byte1, byte2; + UINT i, cnt; + + for (i = 0; i < h; i++) { + pp1 = pic8 + i * w; + pp2 = pp1 + 2; + pp3 = pp1 + w + 2; + + for (; pp2 < pp3; pp2 += 2) { + cnt = 2; + + byte1 = ((pc2nc[*pp1] << 4) & 0xf0) | (pc2nc[*(pp1 + 1)] & 0x0f); + byte2 = ((pc2nc[*pp2] << 4) & 0xf0) | (pc2nc[*(pp2 + 1)] & 0x0f); + + if (byte1 != byte2) { + putc((int)cnt, fp); + putc(byte1, fp); + pp1 = pp2; + } else { + while (cnt <= 254 && pp2 < pp3) { + cnt += 2; + pp2 += 2; + byte2 = ((pc2nc[*pp2] << 4) & 0xf0) | (pc2nc[*(pp2 + 1)] & 0x0f); + if (byte1 != byte2 || cnt >= 254 || pp2 + 2 > pp3) { + if (pp2 + 2 > pp3) + cnt -= 2; + putc((int)cnt, fp); + putc(byte1, fp); + break; + } + } + pp1 = pp2; + } + } + putc(0x00, fp); + putc(0x00, fp); + if (BMP_FERROR(fp)) + return FALSE; + } + putc(0x00, fp); + putc(0x01, fp); + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int line_writeBMPC4(FILE *fp, UCHAR *pic8, UINT w, UINT row, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UCHAR *pp1, *pp2, *pp3, byte1, byte2; + UINT cnt; + + pp1 = pic8 + row * w; + pp2 = pp1 + 2; + pp3 = pp1 + w + 2; + + for (; pp2 < pp3; pp2 += 2) { + cnt = 2; + + byte1 = ((pc2nc[*pp1] << 4) & 0xf0) | (pc2nc[*(pp1 + 1)] & 0x0f); + byte2 = ((pc2nc[*pp2] << 4) & 0xf0) | (pc2nc[*(pp2 + 1)] & 0x0f); + + if (byte1 != byte2) { + putc((int)cnt, fp); + putc(byte1, fp); + pp1 = pp2; + } else { + while (cnt <= 254 && pp2 < pp3) { + cnt += 2; + pp2 += 2; + byte2 = ((pc2nc[*pp2] << 4) & 0xf0) | (pc2nc[*(pp2 + 1)] & 0x0f); + if (byte1 != byte2 || cnt >= 254 || pp2 + 2 > pp3) { + if (pp2 + 2 > pp3) + cnt -= 2; + putc((int)cnt, fp); + putc(byte1, fp); + break; + } + } + pp1 = pp2; + } + } + putc(0x00, fp); + putc(0x00, fp); + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int writeBMP8(FILE *fp, UCHAR *pic8, UINT w, UINT h, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, padw; + UCHAR *pp; + + padw = ((w + 3) / 4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */ + + for (i = 0; i < h; i++) { + pp = pic8 + (i * w); +#ifdef BMP_WRITE_LINE_BY_LINE + if (line_writeBMP8(fp, pp, w, padw, pc2nc) == FALSE) + return FALSE; +#else + /* for (j=0; j= 254 || pp2 + 1 > pp3) { + if (pp2 + 1 > pp3) + cnt--; + putc((int)cnt, fp); + putc(byte1, fp); + break; + } + } + pp1 = pp2; + } + } + putc(0x00, fp); + putc(0x00, fp); + if (BMP_FERROR(fp)) + return FALSE; + } + putc(0x00, fp); + putc(0x01, fp); + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int line_writeBMPC8(FILE *fp, UCHAR *pic8, UINT w, UINT row, UCHAR *pc2nc) +/*---------------------------------------------------------------------------*/ +{ + UCHAR *pp1, *pp2, *pp3, byte1, byte2; + UINT cnt; + + pp1 = pic8 + row * w; + pp2 = pp1 + 1; + pp3 = pp1 + w + 1; + + for (; pp2 < pp3; pp2++) { + cnt = 1; + + byte1 = pc2nc[*pp1]; + byte2 = pc2nc[*pp2]; + + if (byte1 != byte2) { + putc((int)cnt, fp); + putc(byte1, fp); + pp1 = pp2; + } else { + while (cnt <= 254 && pp2 < pp3) { + cnt++; + pp2++; + byte2 = pc2nc[*pp2]; + if (byte1 != byte2 || cnt >= 254 || pp2 + 1 > pp3) { + if (pp2 + 1 > pp3) + cnt--; + putc((int)cnt, fp); + putc(byte1, fp); + break; + } + } + pp1 = pp2; + } + } + putc(0x00, fp); + putc(0x00, fp); + + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int writeBMP24(FILE *fp, UCHAR *pic24, UINT w, UINT h, UCHAR *whence) +/*---------------------------------------------------------------------------*/ +{ + UINT i, j, padb; + LPIXEL *pixel; + UCHAR *pp; + + /* pc2nc not used */ + + padb = (4 - ((w * 3) % 4)) & 0x03; /* # of pad bytes to write at EOscanline */ + + for (i = 0; i < h; i++) { + pp = pic24 + (i * w * 4); + pixel = (LPIXEL *)pp; +#ifdef BMP_WRITE_LINE_BY_LINE + if (line_writeBMP24(fp, pixel, w, padb) == FALSE) + return FALSE; +#else + for (j = 0; j < w; j++) { + putc(pixel->b, fp); + putc(pixel->g, fp); + putc(pixel->r, fp); + + pixel++; + } + for (j = 0; j < padb; j++) + putc(0, fp); +#endif + } + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static int line_writeBMP24(FILE *fp, LPIXEL *pp, UINT w, UINT padb) +/*---------------------------------------------------------------------------*/ +{ + UINT j; + + for (j = 0; j < w; j++) { + putc(pp->b, fp); + putc(pp->g, fp); + putc(pp->r, fp); + + pp++; + } + for (j = 0; j < padb; j++) + putc(0, fp); + if (BMP_FERROR(fp)) + return FALSE; + + return TRUE; +} + +#ifndef __LIBSIMAGE__ + +#ifndef UNUSED_REDUCE_COLORS +/*---------------------------------------------------------------------------*/ +static UCHAR *reduce_colors(UCHAR *buffin, int xsize, int ysize, + UCHAR *rmap, UCHAR *gmap, UCHAR *bmap, int nc) +/*---------------------------------------------------------------------------*/ +{ + LPIXEL *curr_pix, *next_pix, *prev_pix, *buffer; + static LPIXEL *mbuffer = NULL; + static UCHAR *ret_buf = NULL; + static int outbuf_size = 0; + static int buffin_size = 0; + int r1, g1, b1, dim; + int i, j, tmp; + int imax, jmax; + UCHAR *outbuf; + USHORT val; + + dim = xsize * ysize; + + if (dim > outbuf_size) { + if (!ret_buf) + TCALLOC(ret_buf, dim) + else + TREALLOC(ret_buf, dim); + if (!ret_buf) + return NULL; + outbuf_size = dim; + } + + if (dim > buffin_size) { + if (!mbuffer) + TCALLOC(mbuffer, dim) + else + TREALLOC(mbuffer, dim); + if (!ret_buf) + return NULL; + buffin_size = dim; + } + + memcpy(mbuffer, buffin, dim * sizeof(LPIXEL)); + buffer = mbuffer; + outbuf = ret_buf; + + imax = ysize - 1; + jmax = xsize - 1; + + for (i = 0; i < ysize; i++) { + curr_pix = buffer; + buffer += xsize; + next_pix = buffer; + prev_pix = NIL; + + for (j = 0; j < xsize; j++) { + r1 = curr_pix->r; + g1 = curr_pix->g; + b1 = curr_pix->b; + + val = BMP_REDUCE_COLORS(r1, g1, b1); + + *(outbuf++) = (unsigned char)val; + + /* errors on colors */ + r1 -= rmap[val]; + g1 -= gmap[val]; + b1 -= bmap[val]; + + if (j != jmax) + BMP_ADD_ERROR(curr_pix[1], 7) /* RIGHT */ + if (i != imax) /* UP */ + { + BMP_ADD_ERROR(*next_pix, 5) + if (j > 0) + BMP_ADD_ERROR(*prev_pix, 3) /* UP LEFT */ + if (j != jmax) + BMP_ADD_ERROR(next_pix[1], 1) /* UP RIGHT */ + prev_pix = next_pix; + next_pix++; + } + curr_pix++; + } + } + + return ret_buf; +} +#endif + +#endif /* __LIBSIMAGE__ */ + +/*---------------------------------------------------------------------------*/ +int make_bmp_palette(int colors, int grey, UCHAR *r, UCHAR *g, UCHAR *b) +/*---------------------------------------------------------------------------*/ +{ + int i, j, ind, val; + + switch (colors) { + case 2: + for (i = 0; i < 2; i++) + r[i] = g[i] = b[i] = i * 255; + CASE 16 : for (i = 0; i < 16; i++) + { + for (j = 0; j < 16; j++) { + ind = i * 16 + j; + val = i * 16; + r[ind] = g[ind] = b[ind] = val; + } + } + CASE 256 : if (grey) + { + for (i = 0; i < 256; i++) + r[i] = g[i] = b[i] = i; + } + else + { + for (i = 0; i < 256; i++) { + r[i] = BMP_RMAP(i); + g[i] = BMP_GMAP(i); + b[i] = BMP_BMAP(i); + } + } + DEFAULT: + return FALSE; + } + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ +static UINT getshort(FILE *fp) +/*---------------------------------------------------------------------------*/ +{ + int c = getc(fp), + c1 = getc(fp); + + return ((UINT)c) + (((UINT)c1) << 8); +} + +/*---------------------------------------------------------------------------*/ +static UINT getint(FILE *fp) +/*---------------------------------------------------------------------------*/ +{ + int c = getc(fp), + c1 = getc(fp), + c2 = getc(fp), + c3 = getc(fp); + + return (((UINT)c) << 0) + + (((UINT)c1) << 8) + + (((UINT)c2) << 16) + + (((UINT)c3) << 24); +} + +/*---------------------------------------------------------------------------*/ +static void putshort(FILE *fp, int i) +/*---------------------------------------------------------------------------*/ +{ + int c = (((UINT)i)) & 0xff, + c1 = (((UINT)i) >> 8) & 0xff; + + putc(c, fp); + putc(c1, fp); +} + +/*---------------------------------------------------------------------------*/ +static void putint(FILE *fp, int i) +/*---------------------------------------------------------------------------*/ +{ + int c = ((UINT)i) & 0xff, + c1 = (((UINT)i) >> 8) & 0xff, + c2 = (((UINT)i) >> 16) & 0xff, + c3 = (((UINT)i) >> 24) & 0xff; + + putc(c, fp); + putc(c1, fp); + putc(c2, fp); + putc(c3, fp); +} + +/*---------------------------------------------------------------------------*/ + +int writebmp(const MYSTRING filename, int xsize, int ysize, void *buffer, int bpp) +{ + IMAGE img; + img.xsize = xsize; + img.ysize = ysize; + img.buffer = buffer; + switch (bpp) { + case 8: + img.type = BMP_GREY256C; + CASE 32 : img.type = BMP_RGB; + } + return img_write_bmp(filename, &img); +} + +/*---------------------------------------------------------------------------*/ + +#ifdef CICCIO + +int readbmp(const MYSTRING filename, int *xsize, int *ysize, void **buffer) +{ + IMAGE *img; + int retCode = img_read_bmp(filename, &img); + if (retCode != OK) { + *xsize = *ysize = 0; + *buffer = 0; + } else { + *xsize = img->xsize; + *ysize = img->ysize; + *buffer = img->buffer; + img->buffer = 0; + free_img(img); + } + return retCode; +} + +/*---------------------------------------------------------------------------*/ + +int readbmpregion(const MYSTRING filename, void **pimg, int x1, int y1, int x2, int y2, int scale) +{ + IMAGE *img; + + int retCode = img_read_bmp_region(filename, &img, x1, y1, x2, y2, scale); + + if (retCode != OK) { + *pimg = 0; + } else { + *pimg = img->buffer; + free(img); + } + return retCode; +} + +/*---------------------------------------------------------------------------*/ + +int readbmp_size(const MYSTRING fname, int *lx, int *ly) +{ + IMAGE *img; + int retCode = img_read_bmp_generic(fname, BMP_READ_INFO, &img); + if (retCode == OK) { + *lx = img->xsize; + *ly = img->ysize; + free(img); + } + return retCode; +} + +/*---------------------------------------------------------------------------*/ + +int readbmp_bbox(const MYSTRING fname, int *x0, int *y0, int *x1, int *y1) +{ + IMAGE *img; + int retCode = img_read_bmp_generic(fname, BMP_READ_INFO, &img); + if (retCode == OK) { + *x0 = 0; + *x1 = 0; + *x1 = img->xsize - 1; + *y1 = img->ysize - 1; + free(img); + } + return retCode; +} + +#endif diff --git a/toonz/sources/common/tiio/bmp/filebmp.h b/toonz/sources/common/tiio/bmp/filebmp.h new file mode 100644 index 0000000..56562e0 --- /dev/null +++ b/toonz/sources/common/tiio/bmp/filebmp.h @@ -0,0 +1,96 @@ + + +#ifndef __FILEBMP_INCLUDED__ +#define __FILEBMP_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif +/* +#if defined(WIN32) +typedef struct {unsigned char b,g,r,m;} LPIXEL; +#elif defined(__sgi) +typedef struct { unsigned char m,b,g,r; } LPIXEL; +#elif defined (LINUX) +typedef struct { unsigned char r,g,b,m; } LPIXEL; +#else +#error Not yet implemented +#endif +*/ +enum BMP_ERROR_CODE { + OK, + UNSUPPORTED_BMP_FORMAT = -1, + OUT_OF_MEMORY = -2, + UNEXPECTED_EOF = -3, + CANT_OPEN_FILE = -4, + WRITE_ERROR = -5 +}; + +typedef const wchar_t *MYSTRING; + +int writebmp(const MYSTRING filename, int xsize, int ysize, void *buffer, int bpp); +int readbmp(const MYSTRING filename, int *xsize, int *ysize, void **buffer); +int readbmpregion(const MYSTRING fname, void **pimg, int x1, int y1, int x2, int y2, int scale); + +int readbmp_size(const MYSTRING fname, int *lx, int *ly); +int readbmp_bbox(const MYSTRING fname, int *x0, int *y0, int *x1, int *y1); + +typedef enum { + BMP_NONE, + BMP_BW, + BMP_GREY16, + BMP_GREY16C, + BMP_GREY256, + BMP_GREY256C, + BMP_CMAPPED16, + BMP_CMAPPED16C, + BMP_CMAPPED256, + BMP_CMAPPED256C, + BMP_RGB + +} BMP_SUBTYPE; + +typedef struct + { + UINT bfSize; + UINT bfOffBits; + UINT biSize; + UINT biWidth; + UINT biHeight; + UINT biPlanes; + UINT biBitCount; + UINT biCompression; + UINT biSizeImage; + UINT biXPelsPerMeter; + UINT biYPelsPerMeter; + UINT biClrUsed; + UINT biClrImportant; + UINT biFilesize; + UINT biPad; + +} BMP_HEADER; + +int load_bmp_header(FILE *fp, BMP_HEADER **pHd); +int write_bmp_header(FILE *fp, BMP_HEADER *hd); +void release_bmp_header(BMP_HEADER *hd); + +int write_bmp_palette(FILE *fp, int nc, UCHAR *b, UCHAR *g, UCHAR *r); +int make_bmp_palette(int colors, int grey, UCHAR *r, UCHAR *g, UCHAR *b); + +/*BMP_SUBTYPE + bmp_get_colorstyle(IMAGE *img);*/ + +int error_checking_bmp(BMP_HEADER *hd); +int read_bmp_line(FILE *fp, void *line, + UINT w, UINT h, UCHAR **mp, BMP_SUBTYPE type); +int write_bmp_line(FILE *fp, void *line_buffer, + UINT w, UINT row, UCHAR *mp, BMP_SUBTYPE type); +int skip_bmp_lines(FILE *fp, UINT w, UINT rows, int whence, BMP_SUBTYPE type); + +#define BMP_WIN_OS2_OLD 12 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/toonz/sources/common/tiio/compatibility/tfile_io.c b/toonz/sources/common/tiio/compatibility/tfile_io.c new file mode 100644 index 0000000..1995573 --- /dev/null +++ b/toonz/sources/common/tiio/compatibility/tfile_io.c @@ -0,0 +1,99 @@ + + +#include "tfile_io.h" + +#ifdef WIN32 + +#include +#include + +LPSTR AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp) +{ + assert(lpw != NULL); + assert(lpa != NULL); + /* + verify that no illegal character present + since lpa was allocated based on the size of lpw + don t worry about the number of chars +*/ + lpa[0] = '\0'; + WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL); + return lpa; +} + +char *convertWCHAR2CHAR(const wchar_t *fname) +{ + int size = 0; + LPCWSTR lpw = fname; + char *name = NULL; + char *outName = 0; + if (lpw) { + LPSTR pStr = 0; + size = (lstrlenW(lpw) + 1) * 2; + pStr = (LPSTR)malloc(size * sizeof(char)); + name = AtlW2AHelper(pStr, lpw, size, 0); + } + return name; +} + +#else +#include +char *convertWCHAR2CHAR(const wchar_t *wc) +{ + int count = 0; + const wchar_t *ptr = wc; + char *c = 0; + char *buff; + while ((*ptr) != '\0') { + ++count; + ++ptr; + } + c = (char *)malloc((count + 1) * sizeof(char)); + buff = c; + ptr = wc; + while ((*ptr) != '\0') { + *c = *ptr; + ++c; + ++ptr; + } + *c = 0; + return buff; +} +#endif +/*-----------------------------------*/ +#if defined(MACOSX) || defined(LINUX) + +FILE *_wfopen(const wchar_t *fname, const wchar_t *mode) +{ + char *cfname = convertWCHAR2CHAR(fname); + char *cmode = convertWCHAR2CHAR(mode); + + FILE *f = fopen(cfname, cmode); + free(cfname); + free(cmode); + return f; +} + +/*-----------------------------------*/ + +int _wstat(const wchar_t *fname, struct stat *buf) +{ + char *cfname = convertWCHAR2CHAR(fname); + int rc = stat(cfname, buf); + + free(cfname); + return rc; +} + +/*-----------------------------------*/ + +int _wremove(const wchar_t *fname) +{ + char *cfname = convertWCHAR2CHAR(fname); + int rc = remove(cfname); + + free(cfname); + return rc; +} + +#endif diff --git a/toonz/sources/common/tiio/compatibility/tfile_io.h b/toonz/sources/common/tiio/compatibility/tfile_io.h new file mode 100644 index 0000000..c40ea20 --- /dev/null +++ b/toonz/sources/common/tiio/compatibility/tfile_io.h @@ -0,0 +1,35 @@ + + +#ifndef TFILE_IO_H +#define TFILE_IO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef MACOSX +#include +#endif + +char *convertWCHAR2CHAR(const wchar_t *fname); + +#if defined(MACOSX) || defined(LINUX) + +#include +#include +#include + +FILE *_wfopen(const wchar_t *fname, const wchar_t *mode); +int _wstat(const wchar_t *fname, struct stat *buf); +int _wremove(const wchar_t *fname); +#else + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/toonz/sources/common/tiio/movsettings.cpp b/toonz/sources/common/tiio/movsettings.cpp new file mode 100644 index 0000000..5a52730 --- /dev/null +++ b/toonz/sources/common/tiio/movsettings.cpp @@ -0,0 +1,461 @@ + + +#include "texception.h" +#include "tpropertytype.h" +//#include "timageinfo.h" +//#include "tlevel_io.h" +#include "tproperty.h" +#include "tiio.h" + +#if !(defined(x64) || defined(__LP64__)) + +//******************************************************************************* +// 32-bit version +//******************************************************************************* + +#ifdef WIN32 +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif + +#define list List +#define map Map +#define iterator Iterator +#define float_t Float_t + +#include "QTML.h" +#include "Movies.h" +#include "Script.h" +#include "FixMath.h" +#include "Sound.h" + +#include "QuickTimeComponents.h" +#include "tquicktime.h" + +#undef list +#undef map +#undef iterator +#undef float_t + +#else + +#define list List +#define map Map +#define iterator Iterator +#define float_t Float_t +#include +#include +#include +#include + +#undef list +#undef map +#undef iterator +#undef float_t + +#endif +/* +questo file gestisce il salvataggio in un .tnz e il caricamento dei setting dei mov. +viene usato il popup fornito da quicktime, con tutti i suoi setting e i sotto settings. +i setting sono memorizzati da quicktime in un componentInstance. Da qui, possono essere convertiti in un atomContainer, +che e' una struttura simile alla nostra propertyGroup, ma con gli atomi strutturati ad albero. +sono state scritte due funzioni di conversione da atomContainer a propertygroup e viceversa +ogni atom ha un type, id, e numero figli. se numero figli=0 allora l'atomo e'una foglia, +e quindi ha un buffer di dati di valori char. + +ogni atomo viene trasformato in una stringProperty. il nome della stringProperty e' +"type id numeroFigli" +se numerofigli>0, allora la stringProperty ha un valore nullo, e le prossime +numerofigli property contengono i figli; +se numerofigli==0, allora il valore della property contiene il buffer di dati, +convertito in stringa. +ecco coem viene convertito il buffer in stringa: +se ad esempio il buffer e' composto di 3 bytes, buf[0] = 13 buf[1]=0 buf[2]=231 allora la strnga valore sara' "13 0 231" +se ci sono piu 0 consecutivi, vengono memorizzati per salvare spazio come "z count" in cui count e' il numero di 0. +esempio: buf[0] = 13 buf[1]=0 buf[2]=0 buf[3]=0 buf[4]=0 buf5]=231 +allora str = "13 z 4 231" +*/ + +#include "movsettings.h" + +//------------------------------------------------ + +void visitAtoms(const QTAtomContainer &atoms, const QTAtom &parent, TPropertyGroup &pg) +{ + QTAtom curr = 0; + + do { + + if (QTNextChildAnyType(atoms, parent, curr, &curr) != noErr) + assert(false); + + if (curr == 0) + break; + QTAtomType atomType; + QTAtomID id; + + QTGetAtomTypeAndID(atoms, curr, &atomType, &id); + int sonCount = QTCountChildrenOfType(atoms, curr, 0); + + char buffer[1024]; + sprintf(buffer, "%d %d %d", (int)atomType, (int)id, sonCount); + string str(buffer); + + if (sonCount > 0) { + pg.add(new TStringProperty(str, TString())); + visitAtoms(atoms, curr, pg); + } + + else { + long size; + UCHAR *atomData; + if (QTGetAtomDataPtr(atoms, curr, &size, (char **)&atomData) != noErr) + assert(false); + + string strapp; + for (int i = 0; i < size; i++) { + string num; + if (atomData[i] == 0) { + int count = 1; + while ((i + 1) < size && atomData[i + 1] == 0) + i++, count++; + if (count > 1) { + num = toString(count); + strapp = strapp + "z " + num + " "; + continue; + } + } + num = toString(atomData[i]); + + strapp = strapp + string(num) + " "; + } + + //unsigned short*buffer = new unsigned short[size]; + //buffer[size]=0; + //for (i=0; i 0) + compareAtoms(atoms1, curr1, atoms2, curr2); + else { + long size1; + UCHAR *atomData1; + long size2; + UCHAR *atomData2; + if (QTGetAtomDataPtr(atoms1, curr1, &size1, (char **)&atomData1) != noErr) + assert(false); + if (QTGetAtomDataPtr(atoms2, curr2, &size2, (char **)&atomData2) != noErr) + assert(false); + assert(size1 == size2); + for (int i = 0; i < size1; i++) + assert(atomData1[i] == atomData2[i]); + } + } while (curr1 != 0 && curr2 != 0); +} +} + +//------------------------------------------------ + +void fromAtomsToProperties(const QTAtomContainer &atoms, TPropertyGroup &pg) +{ + pg.clear(); + visitAtoms(atoms, kParentAtomIsContainer, pg); +} + +//------------------------------------------------ +void visitprops(TPropertyGroup &pg, int &index, QTAtomContainer &atoms, QTAtom parent) +{ + int count = pg.getPropertyCount(); + while (index < count) { + TStringProperty *p = (TStringProperty *)pg.getProperty(index++); + string str0 = p->getName(); + const char *buf = str0.c_str(); + int atomType, id, sonCount; + sscanf(buf, "%d %d %d", &atomType, &id, &sonCount); + QTAtom newAtom; + if (sonCount == 0) { + wstring appow = p->getValue(); + string appo = toString(appow); + const char *str = appo.c_str(); + + vector buf; + while (strlen(str) > 0) { + if (str[0] == 'z') { + int count = atoi(str + 1); + str += (count < 10) ? 4 : ((count < 100) ? 5 : 6); + while (count--) + buf.push_back(0); + } else { + int val = atoi(str); + assert(val >= 0 && val < 256); + + str += (val < 10) ? 2 : ((val < 100) ? 3 : 4); + buf.push_back(val); + } + } + //const unsigned short*bufs = str1.c_str(); + //UCHAR *bufc = new UCHAR[size]; + //for (int i=0; iwhat) { + case updateEvt: + myEventWindow = (WindowRef)theEvent->message; + // Change the window class + HIWindowChangeClass(myEventWindow,kUtilityWindowClass); + // Activate the window scope + SetWindowActivationScope(myEventWindow,kWindowActivationScopeAll); + // Set the brushed metal theme on the window + SetThemeWindowBackground(myEventWindow,kThemeBrushUtilityWindowBackgroundActive,true); + + break; + } + + return(myEventHandled); +} + +#endif +*/ +//------------------------------------------------ + +void openMovSettingsPopup(TPropertyGroup *props, bool macBringToFront) +{ +#ifdef WIN32 + if (InitializeQTML(0) != noErr) + return; +#endif + + ComponentInstance ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType); + + QTAtomContainer atoms; + QTNewAtomContainer(&atoms); + + fromPropertiesToAtoms(*props, atoms); + + ComponentResult err; + + if ((err = SCSetSettingsFromAtomContainer(ci, atoms)) != noErr) { + CloseComponent(ci); + ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType); + assert(false); + } + + QTDisposeAtomContainer(atoms); + +#ifdef MACOSX + +// Install an external procedure to use a callback filter on the request settings dialog +// On MACOSX we need to change the dialog appearance in order to pop-up in front of the +// toonz main window. +/* +gProcStruct.filterProc = NewSCModalFilterUPP(QTCmpr_FilterProc); +// I don't install any hook +gProcStruct.hookProc = NULL; +gProcStruct.customName[0] = 0; +// I don't use refcon +gProcStruct.refcon = 0; + +// set the current extended procs +SCSetInfo(ci, scExtendedProcsType, &gProcStruct); +*/ +#endif + + err = SCRequestSequenceSettings(ci); + //assert(err==noErr); + QTAtomContainer atomsOut; + + if (SCGetSettingsAsAtomContainer(ci, &atomsOut) != noErr) + assert(false); + + fromAtomsToProperties(atomsOut, *props); + + QTDisposeAtomContainer(atomsOut); + CloseComponent(ci); + + //int dataSize=0, numChildren = 0, numLevels=0; + //retrieveData(settings, kParentAtomIsContainer, dataSize, numChildren, numLevels); +} + +bool Tiio::isQuicktimeInstalled() +{ +#ifdef MACOSX + return true; +#else + + static int ret = -1; + if (ret == -1) + ret = (InitializeQTML(0) == noErr) ? 1 : 0; + + return (ret == 1); + +#endif +} + +#else //x64 + +//******************************************************************************* +// 64-bit proxied version +//******************************************************************************* + +//Toonz includes +#include "tfilepath.h" +#include "tstream.h" + +//tipc includes +#include "tipc.h" +#include "t32bitsrv_wrap.h" + +//MAC-Specific includes +#ifdef MACOSX +#include +#endif + +#include "movsettings.h" + +//--------------------------------------------------------------------------- + +//Using 32-bit background server correspondence to achieve the same result +void openMovSettingsPopup(TPropertyGroup *props, bool unused) +{ + QLocalSocket socket; + if (!tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), 3000, t32bitsrv::srvCmdline(), "_main")) + return; + + //Send the appropriate commands to the server + tipc::Stream stream(&socket); + tipc::Message msg; + + //We'll communicate through temporary files. + stream << (msg << QString("$tmpfile_request") << QString("openMovSets")); + QString res(tipc::readMessage(stream, msg)); + + QString fp; + msg >> fp; + assert(res == "ok" && !fp.isEmpty()); + + TFilePath tfp(fp.toStdWString()); + { + //Save the input props to the temporary file + TOStream os(tfp); + props->saveData(os); + } + + //Invoke the settings popup + stream << (msg << tipc::clr << QString("$openMovSettingsPopup") << fp); + res = tipc::readMessageNB(stream, msg, -1, QEventLoop::ExcludeUserInputEvents); + assert(res == "ok"); + +#ifdef MACOSX + + //Bring this application back to front + ProcessSerialNumber psn = {0, kCurrentProcess}; + SetFrontProcess(&psn); + +#endif //MACOSX + + props->clear(); + { + //Save the input props to the temporary file + TIStream is(tfp); + props->loadData(is); + } + + //Release the temporary file + stream << (msg << tipc::clr << QString("$tmpfile_release") << QString("openMovSets")); + res = tipc::readMessage(stream, msg); + assert(res == "ok"); +} + +//--------------------------------------------------------------------------- + +bool Tiio::isQuicktimeInstalled() +{ + //NOTE: This is *NOT* the same function as IsQuickTimeInstalled(), which is + //implemented locally in the image lib and used there. This function here is + //actually NEVER USED throughout Toonz, so we're placing a dummy + //implementation here. + + assert(false); + return false; +} + +#endif //else diff --git a/toonz/sources/common/tiio/tiio.cpp b/toonz/sources/common/tiio/tiio.cpp new file mode 100644 index 0000000..f60c8b2 --- /dev/null +++ b/toonz/sources/common/tiio/tiio.cpp @@ -0,0 +1,287 @@ + + +#include "tiio.h" + +#include "tiio.h" +#include "tiio_jpg.h" +#include "tproperty.h" + +#include + +//-------------------- +#include +#include +#include + +#ifdef WIN32 +#pragma warning(disable : 4786) +#include +#endif + +#include +//-------------------- + +namespace +{ + +class TiioTable +{ // singleton +public: + typedef std::map + ReaderTable; + + typedef std::map> + WriterTable; + + typedef std::map + VectorReaderTable; + + typedef std::map> + VectorWriterTable; + + typedef std::map + PropertiesTable; + + ReaderTable m_readers; + WriterTable m_writers; + VectorReaderTable m_vectorReaders; + VectorWriterTable m_vectorWriters; + PropertiesTable m_writerProperties; + + TiioTable() { initialize(); } + + static TiioTable *instance() + { + static TiioTable theTable; + return &theTable; + } + ~TiioTable() + { + for (PropertiesTable::iterator it = m_writerProperties.begin(); + it != m_writerProperties.end(); ++it) + delete it->second; + } + + void add(std::string ext, Tiio::ReaderMaker *fn) + { + m_readers[ext] = fn; + } + void add(std::string ext, Tiio::WriterMaker *fn, bool isRenderFormat) + { + m_writers[ext] = std::pair(fn, isRenderFormat); + } + void add(std::string ext, Tiio::VectorReaderMaker *fn) + { + m_vectorReaders[ext] = fn; + } + void add(std::string ext, Tiio::VectorWriterMaker *fn, bool isRenderFormat) + { + m_vectorWriters[ext] = std::pair(fn, isRenderFormat); + } + void addWriterProperties(std::string ext, TPropertyGroup *prop) + { + m_writerProperties[ext] = prop; + } + + Tiio::ReaderMaker *findReader(std::string ext) const + { + ReaderTable::const_iterator it = m_readers.find(ext); + if (it == m_readers.end()) + return 0; + else + return it->second; + } + Tiio::WriterMaker *findWriter(std::string ext) const + { + WriterTable::const_iterator it = m_writers.find(ext); + if (it == m_writers.end()) + return 0; + else + return it->second.first; + } + Tiio::VectorReaderMaker *findVectorReader(std::string ext) const + { + VectorReaderTable::const_iterator it = m_vectorReaders.find(ext); + if (it == m_vectorReaders.end()) + return 0; + else + return it->second; + } + Tiio::VectorWriterMaker *findVectorWriter(std::string ext) const + { + VectorWriterTable::const_iterator it = m_vectorWriters.find(ext); + if (it == m_vectorWriters.end()) + return 0; + else + return it->second.first; + } + TPropertyGroup *findWriterProperties(std::string ext) const + { + PropertiesTable::const_iterator it = m_writerProperties.find(ext); + if (it == m_writerProperties.end()) + return 0; + else + return it->second; + } + + void initialize(); +}; + +} // namespace + +//========================================================= + +void TiioTable::initialize() +{ +} + +Tiio::Reader *Tiio::makeReader(std::string ext) +{ + Tiio::ReaderMaker *reader = TiioTable::instance()->findReader(ext); + if (!reader) + return 0; + else + return reader(); +} + +Tiio::Writer *Tiio::makeWriter(std::string ext) +{ + Tiio::WriterMaker *writer = TiioTable::instance()->findWriter(ext); + if (!writer) + return 0; + else + return writer(); +} + +Tiio::VectorReader *Tiio::makeVectorReader(std::string ext) +{ + Tiio::VectorReaderMaker *reader = TiioTable::instance()->findVectorReader(ext); + if (!reader) + return 0; + else + return reader(); +} + +Tiio::VectorWriter *Tiio::makeVectorWriter(std::string ext) +{ + Tiio::VectorWriterMaker *writer = TiioTable::instance()->findVectorWriter(ext); + if (!writer) + return 0; + else + return writer(); +} + +TPropertyGroup *Tiio::makeWriterProperties(std::string ext) +{ + TPropertyGroup *prop = TiioTable::instance()->findWriterProperties(ext); + if (!prop) + return new TPropertyGroup(); + return prop->clone(); +} + +void Tiio::defineReaderMaker(const char *ext, Tiio::ReaderMaker *fn) +{ + TiioTable::instance()->add(ext, fn); +} + +void Tiio::defineWriterMaker(const char *ext, Tiio::WriterMaker *fn, bool isRenderFormat) +{ + TiioTable::instance()->add(ext, fn, isRenderFormat); +} + +void Tiio::defineVectorReaderMaker(const char *ext, Tiio::VectorReaderMaker *fn) +{ + TiioTable::instance()->add(ext, fn); +} + +void Tiio::defineVectorWriterMaker(const char *ext, Tiio::VectorWriterMaker *fn, bool isRenderFormat) +{ + TiioTable::instance()->add(ext, fn, isRenderFormat); +} + +void Tiio::defineWriterProperties(const char *ext, TPropertyGroup *prop) +{ + TiioTable::instance()->addWriterProperties(ext, prop); +} + +/* +#ifdef WIN32 +int Tiio::openForReading(char *fn) +{ + int fd = _open(fn, _O_BINARY|_O_RDONLY); + if(fd == -1) + { + fprintf(stderr, "File not found\n"); + exit(1); + } + return fd; +} + +void* Tiio::openForReading2(char *fn) +{ + FILE *chan = fopen(fn, "rb"); + if(!chan) + { + fprintf(stderr, "File not found\n"); + exit(1); + } + return (void*)chan; +} + + +int Tiio::openForWriting(char *fn) +{ + int fd = _open( + fn, + _O_BINARY | _O_WRONLY | _O_CREAT | _O_TRUNC, + _S_IREAD | _S_IWRITE); + if(fd == -1) + { + fprintf(stderr, "Can't open file\n"); + exit(1); + } + return fd; +} + +#endif +*/ + +Tiio::Reader::Reader() +{ +} + +Tiio::Reader::~Reader() +{ +} + +int Tiio::Writer::m_bwThreshold = 128; + +Tiio::Writer::Writer() + : m_properties(0) +{ +} + +Tiio::Writer::~Writer() +{ +} + +void Tiio::Writer::getSupportedFormats(QStringList &formats, bool onlyRenderFormats) +{ + TiioTable::VectorWriterTable::const_iterator vit = TiioTable::instance()->m_vectorWriters.begin(); + TiioTable::VectorWriterTable::const_iterator vit_e = TiioTable::instance()->m_vectorWriters.end(); + for (; vit != vit_e; ++vit) + if (onlyRenderFormats && vit->second.second) + formats.push_back(QString::fromStdString(vit->first)); + TiioTable::WriterTable::const_iterator it = TiioTable::instance()->m_writers.begin(); + TiioTable::WriterTable::const_iterator it_e = TiioTable::instance()->m_writers.end(); + for (; it != it_e; ++it) + if (onlyRenderFormats && it->second.second) + formats.push_back(QString::fromStdString(it->first)); +} + +//----------------------------------------------------- + +void Tiio::Writer::setProperties(TPropertyGroup *properties) +{ + m_properties = properties ? properties->clone() : 0; +} diff --git a/toonz/sources/common/tiio/tiio_bmp.cpp b/toonz/sources/common/tiio/tiio_bmp.cpp new file mode 100644 index 0000000..160de2b --- /dev/null +++ b/toonz/sources/common/tiio/tiio_bmp.cpp @@ -0,0 +1,829 @@ + + +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif + +#include "tiio_bmp.h" +// #include "bmp/filebmp.h" +#include "tpixel.h" +#include "tpixelgr.h" +#include "tproperty.h" +//#include "tconvert.h" +#include + +//========================================================= + +namespace +{ + +//========================================================= + +typedef struct + { + UINT bfSize; + UINT bfOffBits; + UINT biSize; + UINT biWidth; + UINT biHeight; + UINT biPlanes; + UINT biBitCount; + UINT biCompression; + UINT biSizeImage; + UINT biXPelsPerMeter; + UINT biYPelsPerMeter; + UINT biClrUsed; + UINT biClrImportant; + UINT biFilesize; + UINT biPad; + +} BMP_HEADER; + +const int BMP_WIN_SIZE = 40; +const int BMP_OS2_SIZE = 64; + +const int BMP_BI_RGB = 0; +const int BMP_BI_RLE8 = 1; +const int BMP_BI_RLE4 = 2; + +//========================================================= + +UINT getshort(FILE *fp) +{ + int c = getc(fp), + c1 = getc(fp); + + return ((UINT)c) + (((UINT)c1) << 8); +} + +//--------------------------------------------------------- + +UINT getint(FILE *fp) +{ + int c = getc(fp), + c1 = getc(fp), + c2 = getc(fp), + c3 = getc(fp); + + return (((UINT)c) << 0) + + (((UINT)c1) << 8) + + (((UINT)c2) << 16) + + (((UINT)c3) << 24); +} + +//--------------------------------------------------------- + +void putshort(FILE *fp, int i) +{ + int c = (((UINT)i)) & 0xff, + c1 = (((UINT)i) >> 8) & 0xff; + + putc(c, fp); + putc(c1, fp); +} + +//--------------------------------------------------------- + +void putint(FILE *fp, int i) +{ + int c = ((UINT)i) & 0xff, + c1 = (((UINT)i) >> 8) & 0xff, + c2 = (((UINT)i) >> 16) & 0xff, + c3 = (((UINT)i) >> 24) & 0xff; + + putc(c, fp); + putc(c1, fp); + putc(c2, fp); + putc(c3, fp); +} + +//========================================================= + +} // namespace + +//========================================================= + +class BmpReader : public Tiio::Reader +{ + FILE *m_chan; + BMP_HEADER m_header; + char *m_line; + int m_lineSize; + TPixel *m_cmap; + bool m_corrupted; + typedef int (BmpReader::*ReadLineMethod)(char *buffer, int x0, int x1, int shrink); + ReadLineMethod m_readLineMethod; + +public: + BmpReader(); + ~BmpReader(); + + void open(FILE *file); + + int readNoLine(char *buffer, int x0, int x1, int shrink); + + void skipBytes(int count) + { + //fseek(m_chan, count, SEEK_CUR); //inefficiente se count è piccolo + for (int i = 0; i < count; i++) { + getc(m_chan); + } + } + + int read1Line(char *buffer, int x0, int x1, int shrink); + int read4Line(char *buffer, int x0, int x1, int shrink); + int read8Line(char *buffer, int x0, int x1, int shrink); + int read8LineRle(char *buffer, int x0, int x1, int shrink); + int read16m555Line(char *buffer, int x0, int x1, int shrink); + int read16m565Line(char *buffer, int x0, int x1, int shrink); + int read24Line(char *buffer, int x0, int x1, int shrink); + int read32Line(char *buffer, int x0, int x1, int shrink); + + void readLine(char *buffer, int x0, int x1, int shrink); + int skipLines(int lineCount) + { + fseek(m_chan, lineCount * m_lineSize, SEEK_CUR); + /* for(int i=0;ibiSize != BMP_WIN_OS2_OLD) + { + // skip ahead to colormap, using biSize + int c = hd->biSize - 40; // 40 bytes read from biSize to biClrImportant + for (int i=0; ibiPad = hd->bfOffBits - (hd->biSize + 14); + } + else + hd->biPad = 0; +*/ + + // load up colormap, if any + if (m_header.biBitCount < 16) { + int cmaplen = (m_header.biClrUsed) + ? m_header.biClrUsed + : 1 << m_header.biBitCount; + assert(cmaplen <= 256); + m_cmap = new TPixel[256]; + TPixel32 *pix = m_cmap; + for (int i = 0; i < cmaplen; i++) { + pix->b = getc(m_chan); + pix->g = getc(m_chan); + pix->r = getc(m_chan); + pix->m = 255; + getc(m_chan); + ++pix; + } + } + + /* + if (hd->biSize != BMP_WIN_OS2_OLD) + { + // Waste any unused bytes between the colour map (if present) + // and the start of the actual bitmap data. + while (hd->biPad > 0) + { + (void) getc(m_chan); + hd->biPad--; + } + } + */ + // get information about the portion of the image to load + //get_info_region(&info, x1, y1, x2, y2, scale, + // (int)hd->biWidth, (int)hd->biHeight, TNZ_BOTLEFT); + + // skip_bmp_lines(fp, hd->biWidth, (unsigned int)(info.startScanRow-1), (unsigned int)SEEK_CUR, subtype); + + int lx = m_info.m_lx; + + switch (m_header.biBitCount) { + case 1: + m_info.m_samplePerPixel = 1; + m_readLineMethod = &BmpReader::read1Line; + m_lineSize = (lx + 7) / 8; + break; + case 4: + m_info.m_samplePerPixel = 1; + if (m_header.biCompression == 0) + m_readLineMethod = &BmpReader::read4Line; + m_lineSize = (lx + 1) / 2; + break; + case 8: + m_info.m_samplePerPixel = 1; + if (m_header.biCompression == 0) + m_readLineMethod = &BmpReader::read8Line; + else if (m_header.biCompression == 1) + m_readLineMethod = &BmpReader::read8LineRle; + m_lineSize = lx; + break; + case 16: + m_info.m_samplePerPixel = 3; + if (m_header.biCompression == 0) // BI_RGB + m_readLineMethod = &BmpReader::read16m555Line; + else if (m_header.biCompression == 3) // BI_BITFIELDS + { + unsigned int rmask = 0, gmask = 0, bmask = 0; + rmask = getint(m_chan); + gmask = getint(m_chan); + bmask = getint(m_chan); + if (gmask == 0x7E0) + m_readLineMethod = &BmpReader::read16m565Line; + else + m_readLineMethod = &BmpReader::read16m555Line; + } + m_lineSize = lx * 2; + break; + case 24: + m_info.m_samplePerPixel = 3; + m_readLineMethod = &BmpReader::read24Line; + m_lineSize = lx * 3; + break; + case 32: + m_info.m_samplePerPixel = 3; + m_readLineMethod = &BmpReader::read32Line; + m_lineSize = lx * 4; + break; + } + m_lineSize += 3 - ((m_lineSize + 3) & 3); + fseek(m_chan, m_header.bfOffBits, SEEK_SET); +} + +//--------------------------------------------------------- + +void BmpReader::readLine(char *buffer, int x0, int x1, int shrink) +{ + (this->*m_readLineMethod)(buffer, x0, x1, shrink); +} + +//--------------------------------------------------------- + +int BmpReader::readNoLine(char *buffer, int x0, int x1, int shrink) +{ + skipBytes(m_lineSize); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read1Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + + //pix += x0; + if (x0 > 0) + skipBytes(x0 / 8); + + TPixel32 *endPix = pix + x1 + 1; + + int value = 0; + int index = x0; + + if (x0 % 8 != 0) { + value = getc(m_chan); + for (index = x0; index < x0 + 8 - (x0 % 8); index += shrink) { + pix[index] = m_cmap[(value >> (7 - ((index) % 8))) & 1]; + } + } + value = getc(m_chan); + int prevBlock = index / 8; + for (int j = index; pix + j < endPix; j += shrink) { + if (j / 8 > prevBlock) + value = getc(m_chan); + prevBlock = j / 8; + pix[j] = m_cmap[(value >> (7 - (j % 8))) & 1]; + } + + /* + while(pix+8<=endPix) + { + value = getc(m_chan); + pix[0] = m_cmap[(value>>7)&1]; + pix[1] = m_cmap[(value>>6)&1]; + pix[2] = m_cmap[(value>>5)&1]; + pix[3] = m_cmap[(value>>4)&1]; + pix[4] = m_cmap[(value>>3)&1]; + pix[5] = m_cmap[(value>>2)&1]; + pix[6] = m_cmap[(value>>1)&1]; + pix[7] = m_cmap[(value>>0)&1]; + pix +=8*shrink; + if(shrink>1) value = getc(m_chan); + //pix+=8*shrink; + //if(pix+81) skipBytes(shrink-1); + } + if(pix=endPix); + for(int j=0; pix+j>(7-j))&1]; + } */ + if ((m_info.m_lx - x1) / 8 > 0) { + skipBytes((m_info.m_lx - x1) / 8); + } + + int bytes = (m_info.m_lx + 7) / 8; + if (m_lineSize - bytes > 0) + skipBytes(m_lineSize - bytes); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read4Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + pix += 2 * x0; + if (x0 > 0) + skipBytes(x0 / 2); + TPixel32 *endPix = pix + x1 - x0 + 1; + + int value; + while (pix + 2 <= endPix) { + value = getc(m_chan); + pix[0] = m_cmap[value & 0xF]; + pix[1] = m_cmap[(value >> 4) & 0xF]; + //pix+=2*shrink; + pix++; + //if(pix+2<=endPix && shrink>1) skipBytes(shrink-1); + } + if (pix < endPix) { + value = getc(m_chan); + pix[0] = m_cmap[value & 0xF]; + } + if ((m_info.m_lx - x1) / 2 > 0) { + skipBytes((m_info.m_lx - x1) / 2); + } + + int bytes = (m_info.m_lx + 1) / 2; + if (m_lineSize - bytes) + skipBytes(m_lineSize - bytes); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read8Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + + if (x0 > 0) + skipBytes(x0); + pix += x0; + TPixel32 *endPix = pix + x1 - x0 + 1; + + while (pix < endPix) { + int value = getc(m_chan); + *pix++ = m_cmap[value]; + if (pix < endPix && shrink > 1) { + skipBytes(shrink - 1); + pix += (shrink - 1); + } + } + + if (m_info.m_lx - x1 - 1 > 0) { + skipBytes(m_info.m_lx - x1 - 1); + } + + if (m_lineSize - m_info.m_lx > 0) + skipBytes(m_lineSize - m_info.m_lx); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read8LineRle(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + if (x0 > 0) + skipBytes(x0); + pix += x0; + TPixel32 *endPix = pix + (x1 - x0 + 1); + + while (pix < endPix) { + int i, count = getc(m_chan); + assert(count >= 0); + if (count == 0) { + int pixels = getc(m_chan); + + assert(pixels >= 0 && pixels != 2); + if (pixels < 3) + return 0; + for (i = 0; i < pixels; i++) + *pix++ = m_cmap[getc(m_chan)]; + if ((1 + 1 + pixels) & 0x1) + getc(m_chan); + } else { + int value = getc(m_chan); + assert(value >= 0); + for (i = 0; i < count; i++) + *pix++ = m_cmap[value]; + } + if (pix < endPix && shrink > 1) { + skipBytes(shrink - 1); + pix += (shrink - 1); + } + } + + if (m_info.m_lx - x1 - 1 > 0) { + skipBytes(m_info.m_lx - x1 - 1); + } + + if (m_lineSize - m_info.m_lx > 0) + skipBytes(m_lineSize - m_info.m_lx); + int val = getc(m_chan); + assert(val == 0); //end scanline or end bmp + val = getc(m_chan); + assert(val == 0 || val == 1); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read16m555Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + if (x0 > 0) + skipBytes(2 * x0); + pix += x0; + TPixel32 *endPix = pix + x1 - x0 + 1; + + while (pix < endPix) { + int v = getshort(m_chan); + int r = (v >> 10) & 0x1F; + int g = (v >> 5) & 0x1F; + int b = v & 0x1F; + pix->r = r << 3 | r >> 2; + pix->g = g << 3 | g >> 2; + pix->b = b << 3 | b >> 2; + pix->m = 255; + pix += shrink; + if (pix < endPix && shrink > 1) + skipBytes(2 * (shrink - 1)); + } + if (m_info.m_lx - x1 - 1 > 0) + skipBytes(2 * (m_info.m_lx - x1 - 1)); + + if (m_lineSize - 2 * m_info.m_lx > 0) + skipBytes(m_lineSize - 2 * m_info.m_lx); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read16m565Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + if (x0 > 0) + skipBytes(2 * x0); + pix += x0; + TPixel32 *endPix = pix + x1 - x0 + 1; + + while (pix < endPix) { + int v = getshort(m_chan); + int r = (v >> 11) & 0x1F; + int g = (v >> 5) & 0x3F; + int b = v & 0x1F; + pix->r = r << 3 | r >> 2; + pix->g = g << 2 | g >> 4; + pix->b = b << 3 | b >> 2; + pix->m = 255; + pix += shrink; + if (pix < endPix && shrink > 1) + skipBytes(2 * (shrink - 1)); + } + if (m_info.m_lx - x1 - 1 > 0) + skipBytes(2 * (m_info.m_lx - x1 - 1)); + + if (m_lineSize - 2 * m_info.m_lx > 0) + skipBytes(m_lineSize - 2 * m_info.m_lx); + return 0; +} +//--------------------------------------------------------- + +int BmpReader::read24Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + if (x0 > 0) + skipBytes(3 * x0); + pix += x0; + TPixel32 *endPix = pix + x1 - x0 + 1; + + while (pix < endPix) { + pix->b = getc(m_chan); + pix->g = getc(m_chan); + pix->r = getc(m_chan); + pix->m = 255; + pix += shrink; + if (pix < endPix && shrink > 1) + skipBytes(3 * (shrink - 1)); + } + if (m_info.m_lx - x1 - 1 > 0) + skipBytes(3 * (m_info.m_lx - x1 - 1)); + + if (m_lineSize - 3 * m_info.m_lx > 0) + skipBytes(m_lineSize - 3 * m_info.m_lx); + return 0; +} + +//--------------------------------------------------------- + +int BmpReader::read32Line(char *buffer, int x0, int x1, int shrink) +{ + TPixel32 *pix = (TPixel32 *)buffer; + if (x0 > 0) + skipBytes(4 * x0); + pix += x0; + TPixel32 *endPix = pix + x1 - x0 + 1; + + while (pix < endPix) { + pix->b = getc(m_chan); + pix->g = getc(m_chan); + pix->r = getc(m_chan); + pix->m = getc(m_chan); + pix += shrink; + if (pix < endPix && shrink > 1) + skipBytes(4 * (shrink - 1)); + } + if (m_info.m_lx - x1 - 1 > 0) + skipBytes(4 * (m_info.m_lx - x1 - 1)); + if (m_lineSize - 4 * m_info.m_lx > 0) + skipBytes(m_lineSize - 4 * m_info.m_lx); + return 0; +} + +//========================================================= + +class BmpWriter : public Tiio::Writer +{ + FILE *m_chan; + int m_bitPerPixel; + int m_compression; + +public: + BmpWriter(); + ~BmpWriter(); + + void open(FILE *file, const TImageInfo &info); + + void flush() { fflush(m_chan); } + + void writeLine(char *buffer); +}; + +//--------------------------------------------------------- + +BmpWriter::BmpWriter() + : m_chan(0) +{ +} + +//--------------------------------------------------------- + +BmpWriter::~BmpWriter() +{ + delete m_properties; +} + +//--------------------------------------------------------- + +void BmpWriter::open(FILE *file, const TImageInfo &info) +{ + m_chan = file; + + m_info = info; + int lx = m_info.m_lx; + int ly = m_info.m_ly; + + if (!m_properties) + m_properties = new Tiio::BmpWriterProperties(); + + TEnumProperty *p = (TEnumProperty *)(m_properties->getProperty("Bits Per Pixel")); + assert(p); + string str = toString(p->getValue()); + m_bitPerPixel = atoi(str.c_str()); + + int cmapSize = 0; + vector *colormap = 0; + + if (m_bitPerPixel == 8) { + TPointerProperty *colormapProp = (TPointerProperty *)(m_properties->getProperty("Colormap")); + if (colormapProp) { + colormap = (vector *)colormapProp->getValue(); + cmapSize = colormap->size(); + } else + cmapSize = 256; + } + + assert(m_bitPerPixel == 8 || m_bitPerPixel == 24); + + int bytePerLine = ((lx * m_bitPerPixel + 31) / 32) * (m_bitPerPixel == 8 ? 1 : 4); + + int fileSize = + 14 // file header + + 40 // info header + + cmapSize * 4 // colormap size + + bytePerLine * ly // image size + ; + int offset = 14 + 40 + cmapSize * 4; + int compression = 0; + int imageSize = bytePerLine * ly; + + putc('B', m_chan); + putc('M', m_chan); // BMP file magic number + + putint(m_chan, fileSize); + putshort(m_chan, 0); // reserved1 + putshort(m_chan, 0); // reserved2 + + putint(m_chan, offset); // offset from BOfile to BObitmap + + putint(m_chan, 40); // size of bitmap info header + putint(m_chan, m_info.m_lx); // width + putint(m_chan, m_info.m_ly); // height + putshort(m_chan, 1); // must be '1' + putshort(m_chan, m_bitPerPixel); // 1,4,8, or 24 + putint(m_chan, compression); // BMP_BI_RGB, BMP_BI_RLE8 or BMP_BI_RLE4 + putint(m_chan, imageSize); // size of raw image data + putint(m_chan, 0); // dpi * 39" per meter + putint(m_chan, 0); // dpi * 39" per meter + putint(m_chan, cmapSize); // colors used in cmap + putint(m_chan, 0); // same as above + + if (colormap) + for (int i = 0; i < (int)colormap->size(); i++) { + putc((*colormap)[i].b, m_chan); + putc((*colormap)[i].g, m_chan); + putc((*colormap)[i].r, m_chan); + putc(0, m_chan); + } + else + for (int i = 0; i < cmapSize; i++) //palette!! + { + putc(i, m_chan); + putc(i, m_chan); + putc(i, m_chan); + putc(0, m_chan); + } +} + +//--------------------------------------------------------- + +void BmpWriter::writeLine(char *buffer) +{ + int lx = m_info.m_lx; + + int j; + + switch (m_bitPerPixel) { + case 24: { + TPixel32 *pix = (TPixel32 *)buffer; + for (j = 0; j < lx; j++) { + putc(pix->b, m_chan); + putc(pix->g, m_chan); + putc(pix->r, m_chan); + ++pix; + } + int bytes = lx * 3; + while (bytes & 3) { + putc(0, m_chan); + bytes++; + } + } + CASE 8: + { + TPixelGR8 *pix = (TPixelGR8 *)buffer; + for (j = 0; j < lx; j++) { + putc(pix->value, m_chan); + ++pix; + } + while (lx & 3) { + putc(0, m_chan); + lx++; + } + } + DEFAULT: + assert(false); + } + ftell(m_chan); +} + +//========================================================= + +Tiio::Reader *Tiio::makeBmpReader() +{ + return new BmpReader(); +} + +//========================================================= + +Tiio::Writer *Tiio::makeBmpWriter() +{ + return new BmpWriter(); +} + +//========================================================= + +Tiio::BmpWriterProperties::BmpWriterProperties() + : m_pixelSize("Bits Per Pixel") +{ + m_pixelSize.addValue(L"24 bits"); + m_pixelSize.addValue(L"8 bits (Greyscale)"); + bind(m_pixelSize); +} diff --git a/toonz/sources/common/tiio/tiio_jpg.cpp b/toonz/sources/common/tiio/tiio_jpg.cpp new file mode 100644 index 0000000..56759a6 --- /dev/null +++ b/toonz/sources/common/tiio/tiio_jpg.cpp @@ -0,0 +1,238 @@ + + +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif +//#include "texception.h" +//#include "tfilepath.h" +//#include "tiio_jpg.h" +//#include "../compatibility/tnz4.h" + +#include "tiio_jpg.h" +#include "tproperty.h" +#include "tpixel.h" + +/* + * Include file for users of JPEG library. + * You will need to have included system headers that define at least + * the typedefs FILE and size_t before you can include jpeglib.h. + * (stdio.h is sufficient on ANSI-conforming systems.) + * You may also wish to include "jerror.h". + */ + +#include +#include + +//========================================================= + +const string Tiio::JpgWriterProperties::QUALITY("Quality"); + +//========================================================= + +extern "C" void tnz_error_exit(j_common_ptr cinfo) +{ + // throw "merda"; +} + +#ifdef CICCIO +JMETHOD(void, error_exit, (j_common_ptr cinfo)); +/* Conditionally emit a trace or warning message */ +JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); +/* Routine that actually outputs a trace or error message */ +JMETHOD(void, output_message, (j_common_ptr cinfo)); +/* Format a message string for the most recent JPEG error or message */ +JMETHOD(void, format_message, (j_common_ptr cinfo, char *buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ +/* Reset error state variables at start of a new image */ +JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); +#endif + +using namespace Tiio; + +JpgReader::JpgReader() : m_chan(0), m_isOpen(false) +{ + memset(&m_cinfo, 0, sizeof m_cinfo); + memset(&m_jerr, 0, sizeof m_jerr); + memset(&m_buffer, 0, sizeof m_buffer); +} + +JpgReader::~JpgReader() +{ + if (m_isOpen) { + try { + jpeg_finish_decompress(&m_cinfo); + jpeg_destroy_decompress(&m_cinfo); + } catch (...) { + } + } + if (m_chan) { + m_chan = 0; + } +} + +Tiio::RowOrder JpgReader::getRowOrder() const { return Tiio::TOP2BOTTOM; } + +void JpgReader::open(FILE *file) +{ + m_cinfo.err = jpeg_std_error(&m_jerr); + m_cinfo.err->error_exit = tnz_error_exit; + + jpeg_create_decompress(&m_cinfo); + m_chan = file; + jpeg_stdio_src(&m_cinfo, m_chan); + bool ret = jpeg_read_header(&m_cinfo, TRUE); + ret = ret && jpeg_start_decompress(&m_cinfo); + if (!ret) + return; + + int row_stride = m_cinfo.output_width * m_cinfo.output_components; + m_buffer = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo, JPOOL_IMAGE, row_stride, 1); + + m_info.m_lx = m_cinfo.output_width; + m_info.m_ly = m_cinfo.output_height; + m_info.m_samplePerPixel = 3; + m_info.m_valid = true; + m_isOpen = true; +} + +void JpgReader::readLine(char *buffer, int x0, int x1, int shrink) +{ + if (m_cinfo.out_color_space == JCS_RGB && m_cinfo.out_color_components == 3) { + int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1); + assert(ret == 1); + unsigned char *src = m_buffer[0]; + TPixel32 *dst = (TPixel32 *)buffer; + dst += x0; + src += 3 * x0; + + int width = (m_cinfo.output_width - 1) / shrink + 1; + if (x1 >= x0) + width = (x1 - x0) / shrink + 1; + + while (--width >= 0) { + dst->r = src[0]; + dst->g = src[1]; + dst->b = src[2]; + dst->m = (char)255; + src += 3 * shrink; + dst += shrink; + } + } else if (m_cinfo.out_color_components == 1) { + int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1); + assert(ret == 1); + unsigned char *src = m_buffer[0]; + TPixel32 *dst = (TPixel32 *)buffer; + + dst += x0; + src += x0; + + int width = (m_cinfo.output_width - 1) / shrink + 1; + if (x1 >= x0) + width = (x1 - x0) / shrink + 1; + + while (--width >= 0) { + dst->r = *src; + dst->g = *src; + dst->b = *src; + dst->m = (char)255; + src += shrink; + dst += shrink; + } + } +} + +int JpgReader::skipLines(int lineCount) +{ + for (int i = 0; i < lineCount; i++) { + int ret = jpeg_read_scanlines(&m_cinfo, m_buffer, 1); + assert(ret == 1); + } + return lineCount; +} + +class JpgWriter : public Tiio::Writer +{ + + struct jpeg_compress_struct m_cinfo; + struct jpeg_error_mgr m_jerr; + FILE *m_chan; + JSAMPARRAY m_buffer; + bool m_headerWritten; + +public: + JpgWriter() + : m_chan(0), m_headerWritten(false) + { + } + + void open(FILE *file, const TImageInfo &info) + { + m_cinfo.err = jpeg_std_error(&m_jerr); + jpeg_create_compress(&m_cinfo); + + m_cinfo.image_width = info.m_lx; + m_cinfo.image_height = info.m_ly; + m_cinfo.input_components = 3; + m_cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&m_cinfo); + if (!m_properties) + m_properties = new Tiio::JpgWriterProperties(); + + jpeg_set_quality(&m_cinfo, ((TIntProperty *)(m_properties->getProperty("Quality")))->getValue(), TRUE); + m_cinfo.smoothing_factor = ((TIntProperty *)(m_properties->getProperty("Smoothing")))->getValue(); + + int row_stride = m_cinfo.image_width * m_cinfo.input_components; + m_buffer = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo, JPOOL_IMAGE, row_stride, 1); + + m_chan = file; + jpeg_stdio_dest(&m_cinfo, m_chan); + } + + ~JpgWriter() + { + jpeg_finish_compress(&m_cinfo); + jpeg_destroy_compress(&m_cinfo); + delete m_properties; + } + + void flush() + { + fflush(m_chan); + } + + Tiio::RowOrder getRowOrder() const { return Tiio::TOP2BOTTOM; } + + void writeLine(char *buffer) + { + if (!m_headerWritten) { + m_headerWritten = true; + jpeg_start_compress(&m_cinfo, TRUE); + } + TPixel32 *src = (TPixel32 *)buffer; + unsigned char *dst = m_buffer[0]; + int lx = m_cinfo.image_width; + while (--lx >= 0) { + dst[0] = src->r; + dst[1] = src->g; + dst[2] = src->b; + dst += 3; + ++src; + } + jpeg_write_scanlines(&m_cinfo, m_buffer, 1); + } +}; + +//---- +//---- +//---- + +Tiio::Reader *Tiio::makeJpgReader() +{ + return new JpgReader(); +} + +Tiio::Writer *Tiio::makeJpgWriter() +{ + return new JpgWriter(); +} diff --git a/toonz/sources/common/tiio/tiio_jpg_util.cpp b/toonz/sources/common/tiio/tiio_jpg_util.cpp new file mode 100644 index 0000000..f782486 --- /dev/null +++ b/toonz/sources/common/tiio/tiio_jpg_util.cpp @@ -0,0 +1,75 @@ + + +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif + +#include "tiio_jpg_util.h" +//#include "tiio.h" +#include "tiio_jpg.h" +#include "tproperty.h" +#include +#include "tfilepath_io.h" +#include "tsystem.h" + +void Tiio::createJpg( + std::vector &buffer, + const TRaster32P &ras, + int quality) +{ + //FILE *chan = tmpfile(); + TFilePath fname = TSystem::getUniqueFile(); + FILE *chan = fopen(fname, "w+b"); + if (chan == 0) + throw TException(L". Can not create " + fname.getWideString()); + + assert(chan); + assert(ras); + assert(0 <= quality && quality <= 100); + fflush(chan); + Tiio::Writer *writer = Tiio::makeJpgWriter(); + assert(writer); + TPropertyGroup *pg = writer->getProperties(); + if (!pg) { + writer->setProperties(new JpgWriterProperties()); + pg = writer->getProperties(); + } + + TProperty *qualityProp = pg->getProperty(JpgWriterProperties::QUALITY); + assert(qualityProp); + TIntProperty *ip = dynamic_cast(qualityProp); + assert(ip); + ip->setValue(quality); + int lx = ras->getLx(); + int ly = ras->getLy(); + assert(lx > 0 && ly > 0); + writer->open(chan, TImageInfo(lx, ly)); + + ras->lock(); + for (int y = 0; y < ly; y++) + writer->writeLine((char *)ras->getRawData(0, ly - 1 - y)); + ras->unlock(); + writer->flush(); + delete writer; + fclose(chan); + + //lo chiudo e lo riapro: altrimenti, gettava male la filesize. boh. + FILE *chan1 = fopen(fname, "rb"); + if (chan1 == 0) + throw TException(L". Can not create " + fname.getWideString()); + int ret = fseek(chan1, 0, SEEK_END); + assert(ret == 0); + int fileSize = ftell(chan1); + + buffer.resize(fileSize); + ret = fseek(chan1, 0, SEEK_SET); + assert(ret == 0); + for (int i = 0; i < fileSize; i++) { + int c = fgetc(chan1); + assert(!feof(chan1)); + buffer[i] = c; + } + + fclose(chan1); + TSystem::deleteFile(fname); +} diff --git a/toonz/sources/common/tiio/tiio_std.cpp b/toonz/sources/common/tiio/tiio_std.cpp new file mode 100644 index 0000000..fa9064d --- /dev/null +++ b/toonz/sources/common/tiio/tiio_std.cpp @@ -0,0 +1,23 @@ + + +#include "tiio_jpg.h" +#include "tiio_bmp.h" +#include "tiio_std.h" +#include "tfiletype.h" +#include "tflash.h" + +using namespace Tiio; +using namespace TFileType; + +void Tiio::defineStd() +{ + defineReaderMaker("jpg", Tiio::makeJpgReader); + defineWriterMaker("jpg", Tiio::makeJpgWriter, true); + declare("jpg", TFileType::RASTER_IMAGE); + defineWriterProperties("jpg", new JpgWriterProperties()); + + defineReaderMaker("bmp", Tiio::makeBmpReader); + defineWriterMaker("bmp", Tiio::makeBmpWriter, true); + declare("bmp", TFileType::RASTER_IMAGE); + defineWriterProperties("bmp", new BmpWriterProperties()); +} diff --git a/toonz/sources/common/timage/timage.cpp b/toonz/sources/common/timage/timage.cpp new file mode 100644 index 0000000..810307c --- /dev/null +++ b/toonz/sources/common/timage/timage.cpp @@ -0,0 +1,50 @@ + + +#include "timage.h" + +#ifndef TNZCORE_LIGHT + +#include "tpalette.h" + +void TImage::setPalette(TPalette *palette) +{ + if (m_palette == palette) + return; + if (palette) + palette->addRef(); + if (m_palette) + m_palette->release(); + m_palette = palette; +} + +TImage::~TImage() +{ + if (m_palette) + m_palette->release(); +} + +#else + +class TPalette +{ +}; + +void TImage::setPalette(TPalette *palette) +{ + assert(false); +} + +TImage::~TImage() +{ +} + +#endif + +//#include "tiio.h" + +DEFINE_CLASS_CODE(TImage, 4) + +TImage::TImage() + : TSmartObject(m_classCode), m_palette(0) +{ +} diff --git a/toonz/sources/common/timage/tlevel.cpp b/toonz/sources/common/timage/tlevel.cpp new file mode 100644 index 0000000..4c6a212 --- /dev/null +++ b/toonz/sources/common/timage/tlevel.cpp @@ -0,0 +1,100 @@ + + +#include "tlevel.h" +#include "tpalette.h" + +DEFINE_CLASS_CODE(TLevel, 7) + +//------------------------------------------------- + +TLevel::TLevel() + : TSmartObject(m_classCode), m_name(""), m_table(new Table()), m_palette(0) +{ +} + +//------------------------------------------------- + +TLevel::~TLevel() +{ + if (m_palette) + m_palette->release(); + delete m_table; +} + +//------------------------------------------------- + +string TLevel::getName() const +{ + return m_name; +} + +//------------------------------------------------- + +void TLevel::setName(string name) +{ + m_name = name; +} + +//------------------------------------------------- + +const TImageP &TLevel::frame(const TFrameId fid) +{ + const static TImageP none; + assert(m_table); + Iterator it = m_table->find(fid); + if (it == m_table->end()) + return none; // (*m_table)[fid]; + else + return it->second; +} + +//------------------------------------------------- + +void TLevel::setFrame(const TFrameId &fid, const TImageP &img) +{ + assert(m_table); + if (img) + img->setPalette(getPalette()); + (*m_table)[fid] = img; +} + +//------------------------------------------------- + +/* +// brutto e inefficiente +int TLevel::getIndex(const TFrameId fid) +{ + int index = 0; + for(Iterator it = m_table->begin(); it != m_table->end(); it++, index++) + if(it->first == fid) return index; + return -1; +} + +*/ + +//------------------------------------------------- + +TPalette *TLevel::getPalette() +{ + return m_palette; +} + +//------------------------------------------------- + +void TLevel::setPalette(TPalette *palette) +{ + if (m_palette == palette) + return; + if (palette) + palette->addRef(); + if (m_palette) + m_palette->release(); + m_palette = palette; + for (Iterator it = begin(); it != end(); ++it) { + TImageP &img = it->second; + if (img) + img->setPalette(m_palette); + } +} + +//------------------------------------------------- diff --git a/toonz/sources/common/timage_io/timage_io.cpp b/toonz/sources/common/timage_io/timage_io.cpp new file mode 100644 index 0000000..1ed32f2 --- /dev/null +++ b/toonz/sources/common/timage_io/timage_io.cpp @@ -0,0 +1,840 @@ + + +// TnzCore includes +#include "tsystem.h" +#include "timage_io.h" +#include "trop.h" +#include "tconvert.h" +#include "tvectorimage.h" +#include "ttoonzimage.h" +#include "tproperty.h" +#include "trasterimage.h" +#include "tiio.h" +#include "tfilepath_io.h" + +// boost includes +#include + +// OS-specific includes +#ifdef WIN32 +#pragma warning(disable : 4996) +#include +#endif +#ifdef LINUX +#include +#endif + +// System V includes +#include // didn't even know Windows had them :D +#include // btw, why are they here? + +DEFINE_CLASS_CODE(TImageReader, 5) +DEFINE_CLASS_CODE(TImageWriter, 6) + +//----------------------------------------------------------- + +std::map ImageReaderTable; +std::map> ImageWriterTable; + +//----------------------------------------------------------- + +bool TImageReader::m_safeMode = false; + +//----------------------------------------------------------- + +TImageReader::TImageReader(const TFilePath &path) + : TSmartObject(m_classCode), m_path(path), m_reader(0), m_vectorReader(0), m_readGreytones(true), m_file(NULL), m_is64BitEnabled(false), m_shrink(1), m_region(TRect()) +{ +} + +//----------------------------------------------------------- + +void TImageReader::doReadGraytones(bool readThem) +{ + m_readGreytones = readThem; +} + +//----------------------------------------------------------- + +TImageReader::~TImageReader() +{ + close(); +} + +//----------------------------------------------------------- + +bool TImageReader::isOpen() const +{ + return m_file != NULL; +} + +//----------------------------------------------------------- + +void TImageReader::close() +{ + delete m_reader; + delete m_vectorReader; + + if (m_file != NULL) { + int err = 0; + err = fclose(m_file); + + assert(err == 0); //il file e' stato chiuso senza errori + } + + m_file = NULL; + m_reader = 0; + m_vectorReader = 0; +} + +//----------------------------------------------------------- + +/*! + Opens for reading using \b fopen(). +*/ +void TImageReader::getTzpPaletteColorNames(map> &pltColorNames) +{ + if (!m_file) + open(); + + if (!m_file) + return; + + assert(m_reader); + m_reader->getTzpPaletteColorNames(pltColorNames); +} + +//----------------------------------------------------------- + +void TImageReader::open() +{ + assert(m_file == NULL); + + string type = toLower(m_path.getType()); + m_file = fopen(m_path, "rb"); //Opens for reading. If the file does not exist or cannot be found, the fopen_s call fails + + if (m_file == NULL) //Non dovrebbe mai andarci! + close(); //close() chiude il file se e' aperto e setta m_file a NULL. + else { + try { + m_reader = Tiio::makeReader(type); + if (m_reader) + m_reader->open(m_file); + else { + m_vectorReader = Tiio::makeVectorReader(type); + if (m_vectorReader) + m_vectorReader->open(m_file); + else + throw TImageException(m_path, "Image format not supported"); + } + } catch (TException &e) { + close(); //close() chiude il file e setta m_file a NULL. + QString msg = QString::fromStdWString(e.getMessage()); + if (msg == QString("Old 4.1 Palette")) + throw e; + } catch (string str) { + if (str == "Tiff file closed") + m_file = NULL; + } + } +} + +//=========================================================== + +void TImageReader::setShrink(int shrink) +{ + m_shrink = shrink; +} + +//----------------------------------------------------------- + +TImageReaderP::TImageReaderP(const TFilePath &path) +{ + m_pointer = new TImageReader(path); + m_pointer->addRef(); + m_pointer->open(); +} + +//----------------------------------------------------------- + +TImageP TImageReader::load() +{ + + TImageP image = load0(); + if (!image) + return TImageP(); + + TImageInfo info = m_reader->getImageInfo(); + if (info.m_lx <= 0 || info.m_ly <= 0) + return TImageP(); + + return image; +} + +//============================================================ + +template +struct pixel_traits { +}; + +template <> +struct pixel_traits { + typedef TPixel32 rgbm_pixel_type; + typedef char buffer_type; +}; + +template <> +struct pixel_traits { + typedef TPixel64 rgbm_pixel_type; + typedef short buffer_type; +}; + +template <> +struct pixel_traits { + typedef TPixel32 rgbm_pixel_type; + typedef char buffer_type; +}; + +template <> +struct pixel_traits { + typedef TPixel64 rgbm_pixel_type; + typedef short buffer_type; +}; + +template <> +struct pixel_traits { + typedef TPixel32 rgbm_pixel_type; + typedef char buffer_type; +}; + +//------------------------------------------------------------------------------------ + +template +void copyLine(typename pixel_traits::rgbm_pixel_type *lineIn, Pix *lineOut, int x0, int length, int shrink) +{ + lineIn += x0; + + for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) + memcpy(lineOut, lineIn, sizeof(Pix)); +} + +template <> +void copyLine(TPixel32 *lineIn, TPixelGR8 *lineOut, int x0, int length, int shrink) +{ + lineIn += x0; + + for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) + lineOut->value = lineIn->r; +} + +template <> +void copyLine(TPixel64 *lineIn, TPixelGR16 *lineOut, int x0, int length, int shrink) +{ + lineIn += x0; + + for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) + lineOut->value = lineIn->r; +} + +//------------------------------------------------------------------------------------ + +template +void readRaster_copyLines(const TRasterPT &ras, Tiio::Reader *reader, + int x0, int y0, int x1, int y1, + int inLx, int inLy, int shrink) +{ + typedef typename pixel_traits::buffer_type buffer_type; + typedef typename pixel_traits::rgbm_pixel_type rgbm_pixel_type; + + int linesToSkip = shrink - 1; + + buffer_type *lineBuffer = (buffer_type *)malloc(inLx * sizeof(rgbm_pixel_type)); + if (!lineBuffer) + return; + + rgbm_pixel_type *lineIn = (rgbm_pixel_type *)lineBuffer; + + if (reader->getRowOrder() == Tiio::BOTTOM2TOP) { + int start = reader->skipLines(y0); + int stop = y1 + 1; + + for (int y = start; y < stop; ++y) { + reader->readLine(lineBuffer, x0, x1, shrink); + + if (y >= y0 && y <= y1 && (y - y0) % shrink == 0) { + Pix *line = (Pix *)ras->getRawData(0, (y - y0) / shrink); + copyLine(lineIn, line, x0, ras->getLx(), shrink); + } + + if (linesToSkip > 0 && y + linesToSkip < inLy) + y += reader->skipLines(linesToSkip); + } + } else // TOP2BOTTOM + { + reader->skipLines(inLy - y1 - 1); + + for (int y = y1; y >= y0; --y) { + reader->readLine(lineBuffer, x0, x1, shrink); + + if ((y - y0) % shrink == 0) { + Pix *line = (Pix *)ras->getRawData(0, (y - y0) / shrink); + copyLine(lineIn, line, x0, ras->getLx(), shrink); + } + + if (linesToSkip > 0 && y - linesToSkip > 0) + y -= reader->skipLines(linesToSkip); + } + } + + free(lineBuffer); +} + +//------------------------------------------------------------------------------------ + +template +void readRaster(const TRasterPT &ras, Tiio::Reader *reader, + int x0, int y0, int x1, int y1, + int inLx, int inLy, int shrink) +{ + typedef typename pixel_traits::buffer_type buffer_type; + + if (shrink == 1) { + // Direct read + ras->lock(); + + ptrdiff_t linePad = -x0 * ras->getPixelSize(); + + if (reader->getRowOrder() == Tiio::BOTTOM2TOP) { + int start = reader->skipLines(y0); + int stop = y1 + 1; + + for (int y = start; y < stop; ++y) + if (y >= y0 && y <= y1) { + buffer_type *line = (buffer_type *)(ras->getRawData(0, y - y0) + linePad); + reader->readLine(line, x0, x1, 1); + } + } else // TOP2BOTTOM + { + reader->skipLines(inLy - y1 - 1); + + for (int y = y1; y >= y0; --y) { + buffer_type *line = (buffer_type *)(ras->getRawData(0, y - y0) + linePad); + reader->readLine(line, x0, x1, 1); + } + } + + ras->unlock(); + } else + readRaster_copyLines(ras, reader, x0, y0, x1, y1, inLx, inLy, shrink); +} + +//------------------------------------------------------------------------------------ + +TImageP TImageReader::load0() +{ + if (!m_reader && !m_vectorReader) + open(); + + if (m_reader) { + TImageInfo info = m_reader->getImageInfo(); + if (info.m_lx <= 0 || info.m_ly <= 0) + return TImageP(); + + // Initialize raster info + assert(m_shrink > 0); + + // Build loading rect + int x0 = 0; + int x1 = info.m_lx - 1; + int y0 = 0; + int y1 = info.m_ly - 1; + + if (!m_region.isEmpty()) { + // Intersect with the externally specified loading region + + x0 = tmax(x0, m_region.x0); + y0 = tmax(y0, m_region.y0); + x1 = tmin(x1, m_region.x1); + y1 = tmin(y1, m_region.y1); + + if (x0 >= x1 || y0 >= y1) + return TImageP(); + } + + if (m_shrink > 1) { + // Crop to shrink multiples + x1 -= (x1 - x0) % m_shrink; + y1 -= (y1 - y0) % m_shrink; + } + + assert(x0 <= x1 && y0 <= y1); + + TDimension imageDimension = TDimension((x1 - x0) / m_shrink + 1, (y1 - y0) / m_shrink + 1); + + if (m_path.getType() == "tzp" || m_path.getType() == "tzu") { + // Colormap case + + TRasterCM32P ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + // Build the savebox + TRect saveBox(info.m_x0, info.m_y0, info.m_x1, info.m_y1); + if (!m_region.isEmpty()) { + // Intersect with the loading rect + if (m_region.overlaps(saveBox)) { + saveBox *= m_region; + + int sbx0 = saveBox.x0 - m_region.x0; + int sby0 = saveBox.y0 - m_region.y0; + int sbx1 = sbx0 + saveBox.getLx() - 1; + int sby1 = sby0 + saveBox.getLy() - 1; + assert(sbx0 >= 0); + assert(sby0 >= 0); + assert(sbx1 >= 0); + assert(sby1 >= 0); + + saveBox = TRect(sbx0, sby0, sbx1, sby1); + } else + saveBox = TRect(0, 0, 1, 1); + } + + if (m_shrink > 1) { + saveBox.x0 = saveBox.x0 / m_shrink; + saveBox.y0 = saveBox.y0 / m_shrink; + saveBox.x1 = saveBox.x1 / m_shrink; + saveBox.y1 = saveBox.y1 / m_shrink; + } + + TToonzImageP ti(ras, ras->getBounds() * saveBox); + ti->setDpi(info.m_dpix, info.m_dpiy); + + return ti; + } else if (info.m_bitsPerSample >= 8) { + // Common byte-based rasters (see below, we have black-and-white bit-based images too) + + if (info.m_samplePerPixel == 1 && m_readGreytones) { + // Greymap case + // NOTE: Uses a full 32-bit raster first, and then copies down to the GR8 + + // Observe that GR16 file images get immediately down-cast to GR8... + // Should we implement that too? + + TRasterGR8P ras(imageDimension); + readRaster_copyLines(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + TRasterImageP ri(ras); + ri->setDpi(info.m_dpix, info.m_dpiy); + + return ri; + } + + // assert(info.m_samplePerPixel == 3 || info.m_samplePerPixel == 4); + + TRasterP _ras; + if (info.m_bitsPerSample == 16) { + if (m_is64BitEnabled || m_path.getType() != "tif") { + // Standard 64-bit case. + + // Also covers down-casting to 32-bit from a 64-bit image file whenever + // not a tif file (see below). + + TRaster64P ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + _ras = ras; + } else { + // The Tif reader has got an automatically down-casting readLine(char*, ...) + // in case the input file is 64-bit. The advantage is that no intermediate + // 64-bit raster is required in this case. + + TRaster32P ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + _ras = ras; + } + } else if (info.m_bitsPerSample == 8) { + // Standard 32-bit case + TRaster32P ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + _ras = ras; + } else + throw TImageException(m_path, "Image format not supported"); + + // 64-bit to 32-bit conversions if necessary (64 bit images not allowed) + if (!m_is64BitEnabled && (TRaster64P)_ras) { + TRaster32P raux(_ras->getLx(), _ras->getLy()); + TRop::convert(raux, _ras); + _ras = raux; + } + + // Return the image + TRasterImageP ri(_ras); + ri->setDpi(info.m_dpix, info.m_dpiy); + + return ri; + } else if (info.m_samplePerPixel == 1 && info.m_valid == true) { + // Previously dubbed as 'Palette cases'. No clue about what is this... :| + + TRaster32P ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + TRasterImageP ri(ras); + ri->setDpi(info.m_dpix, info.m_dpiy); + + return ri; + } else if (info.m_samplePerPixel == 1 && m_readGreytones) { + // Black-and-White case, I guess. Standard greymaps were considered above... + + TRasterGR8P ras(imageDimension); + readRaster_copyLines(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); + + TRasterImageP ri(ras); + ri->setDpi(info.m_dpix, info.m_dpiy); + + if (info.m_bitsPerSample == 1) // I suspect this always evaluates true... + ri->setScanBWFlag(true); + + return ri; + } else + return TImageP(); + } else if (m_vectorReader) { + TVectorImage *vi = m_vectorReader->read(); + return TVectorImageP(vi); + } else + return TImageP(); +} + +//----------------------------------------------------------- + +TImageWriter::TImageWriter(const TFilePath &path) + : TSmartObject(m_classCode), m_path(path), m_writer(0), m_vectorWriter(0), m_properties(0) +{ +} + +//----------------------------------------------------------- + +TImageWriter::~TImageWriter() +{ + delete m_writer; + delete m_vectorWriter; + delete m_properties; +} + +//----------------------------------------------------------- + +void TImageWriter::setProperties(const TPropertyGroup *g) +{ + if (m_properties) { + assert(m_properties != g); + delete m_properties; + } + m_properties = g ? g->clone() : 0; +} + +//----------------------------------------------------------- + +void convertForWriting(TRasterP &ras, const TRasterP &rin, int bpp) +{ + switch (bpp) { + case 1: + __OR 8: + { + ras = TRasterGR8P(rin->getSize()); + TRop::convert(ras, rin); + } + + CASE 24 : __OR 32: + { + ras = TRaster32P(rin->getSize()); + TRop::convert(ras, rin); + } + + CASE 48 : __OR 64: + { + ras = TRaster64P(rin->getSize()); + TRop::convert(ras, rin); + } + + DEFAULT: + assert(false); + } +} + +//----------------------------------------------------------- + +void TImageWriter::save(const TImageP &img) +{ + const std::string &type = toLower(m_path.getType()); + + Tiio::Writer *writer = Tiio::makeWriter(type); + if (!writer) + throw TImageException(m_path, "unsupported format for raster images"); + + writer->setProperties(m_properties); + + FILE *file = fopen(m_path, "wb"); + if (file == NULL) + throw TImageException(m_path, "Can't write file"); + + if (TRasterImageP ri = img) { + TImageInfo info; + TRasterP ras; + + TRasterGR8P rasGr = ri->getRaster(); + TRaster32P ras32 = ri->getRaster(); + TRaster64P ras64 = ri->getRaster(); + + TEnumProperty *p = m_properties ? (TEnumProperty *)m_properties->getProperty("Bits Per Pixel") + : 0; + + if (p && ri->isScanBW()) { + const std::vector &range = p->getRange(); + p->setValue(range[2]); // Horrible. See tiio_tif.cpp (732 or near) -.-' + } + + int bpp = p ? atoi((toString(p->getValue()).c_str())) : 32; + + // bpp 1 8 16 24 32 40 48 56 64 + int spp[] = {1, 1, 1, 4, 4, 0, 4, 0, 4}; // 0s are for pixel sizes which are normally unsupported + int bps[] = {1, 8, 16, 8, 8, 0, 16, 0, 16}; // by image formats, let alone by Toonz raster ones. + // The 24 and 48 cases get automatically promoted to 32 and 64. + int bypp = bpp / 8; + assert(bypp < boost::size(spp) && spp[bypp] && bps[bypp]); + + info.m_samplePerPixel = spp[bypp]; + info.m_bitsPerSample = bps[bypp]; + + if (rasGr) { + if (bypp < 2) // Seems 16 bit greymaps are not handled... why? + ras = rasGr; // we do have a Toonz raster for those... >:| + else + convertForWriting(ras, rasGr, bpp); + } else if (ras32) { + if (bpp == 32 || bpp == 24) + ras = ras32; + else + convertForWriting(ras, ras32, bpp); + } else if (ras64) { + if (bpp == 64 || bpp == 48) + ras = ras64; + else + convertForWriting(ras, ras64, bpp); + } else { + fclose(file); + throw TImageException(m_path, "unsupported raster type"); + } + + info.m_lx = ras->getLx(); + info.m_ly = ras->getLy(); + + ri->getDpi(info.m_dpix, info.m_dpiy); + + if (writer->getProperties() && m_properties) + writer->getProperties()->setProperties(m_properties); + + writer->open(file, info); + + ras->lock(); + if (writer->getRowOrder() == Tiio::BOTTOM2TOP) { + if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) + for (int i = 0; i < ras->getLy(); i++) + writer->writeLine((char *)ras->getRawData(0, i)); + else + for (int i = 0; i < ras->getLy(); i++) + writer->writeLine((short *)ras->getRawData(0, i)); + } else { + if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) + for (int i = ras->getLy() - 1; i >= 0; i--) + writer->writeLine((char *)ras->getRawData(0, i)); + else + for (int i = ras->getLy() - 1; i >= 0; i--) + writer->writeLine((short *)ras->getRawData(0, i)); + } + + ras->unlock(); + + writer->flush(); + delete writer; + } else if (TVectorImageP vi = img) { + Tiio::VectorWriter *writer = Tiio::makeVectorWriter(type); + if (!writer) { + fclose(file); + throw TImageException(m_path, "unsupported format for vector images"); + } + + writer->open(file); + writer->write(vi.getPointer()); + + delete writer; + } else { + fclose(file); + throw TImageException(m_path, "Can't write file"); + } + + fclose(file); +} + +//----------------------------------------------------------- + +TImageWriterP::TImageWriterP(const TFilePath &path) +{ + m_pointer = new TImageWriter(path); + m_pointer->addRef(); +} + +//============================================================ +// +// Helper functions statiche +// +//============================================================ + +bool TImageReader::load(const TFilePath &path, TRasterP &raster) +{ + raster = TRasterP(); + TImageReaderP ir(path); + if (!ir) + return false; + TImageP img = ir->load(); + if (!img) + return false; + + TRasterImageP ri(img); + if (!ri) + return false; + + raster = ri->getRaster(); + return true; +} + +//----------------------------------------------------------- + +//----------------------------------------------------------- + +void TImageReader::load(const TRasterP &ras, const TPoint &pos, int shrinkX, int shrinkY) +{ + TImageP srcImage = load(); + TRasterImageP srcRasImage = srcImage; + TRaster32P srcRaster = srcRasImage->getRaster(); + /* +TRaster32P clippedRas = srcRaster->extractT + (shrinkX*pos.x, shrinkY*pos.y, + (pos.x + ras->getLx()) * shrinkX - 1, (pos.y + ras->getLy()) * shrinkY - 1); + +if (shrinkX != 1 || shrinkY != 1) +{ + TRaster32P ras32 = ras; + if (ras32) + ras32->fill(TPixel32::Transparent); + TRop::resample(ras, clippedRas, TScale(1./shrinkX, 1./shrinkY), TRop::ClosestPixel); +} +else*/ + ras->copy(srcRaster); +} + +//----------------------------------------------------------- + +bool TImageReader::load(const TFilePath &path, TImageP &image) +{ + image = TImageReaderP(path)->load(); + return image; +} + +//----------------------------------------------------------- + +void TImageWriter::save(const TFilePath &path, TRasterP raster) +{ + TRasterImageP rasImage(raster); + TImageWriterP(path)->save(TImageP(rasImage)); +} + +//----------------------------------------------------------- + +void TImageWriter::save(const TFilePath &path, const TImageP &image) +{ + TImageWriterP(path)->save(image); +} + +//=========================================================== +// +// funzioni per la registrazione dei formati (chiamate dal Plugin) +// +//=========================================================== + +void TImageReader::define( + QString extension, + TImageReaderCreateProc *proc) +{ + ImageReaderTable[extension] = proc; +} + +//----------------------------------------------------------- + +void TImageWriter::define( + QString extension, + TImageWriterCreateProc *proc, bool isRenderFormat) +{ + ImageWriterTable[extension] = std::pair(proc, isRenderFormat); +} + +//----------------------------------------------------------- + +void TImageReader::getSupportedFormats(QStringList &names) +{ + for (std::map::iterator it = ImageReaderTable.begin(); + it != ImageReaderTable.end(); + ++it) { + names.push_back(it->first); + } +} + +//----------------------------------------------------------- + +void TImageWriter::getSupportedFormats(QStringList &names, bool onlyRenderFormat) +{ + for (std::map>::iterator it = ImageWriterTable.begin(); + it != ImageWriterTable.end(); + ++it) { + if (!onlyRenderFormat || it->second.second) + names.push_back(it->first); + } +} + +//----------------------------------------------------------- + +const TImageInfo *TImageReader::getImageInfo() const +{ + if (m_reader) + return &(m_reader->getImageInfo()); + else + return 0; +} + +//----------------------------------------------------------- + +//=========================================================== +// +// Eccezioni +// +//=========================================================== + +TImageException::TImageException(const TFilePath &fp, const string &msg) + : TException(msg), m_fp(fp) +{ +} + +//----------------------------------------------------------- + +TString TImageException::getMessage() const +{ + return m_fp.getWideString() + L": " + TException::getMessage(); +} + +//=========================================================== + +TImageVersionException::TImageVersionException(const TFilePath &fp, int major, int minor) + : TException(L"The file " + fp.getWideString() + + L" was generated by a newer version of Toonz and cannot be loaded."), + m_fp(fp), m_major(major), m_minor(minor) +{ +} diff --git a/toonz/sources/common/timage_io/tlevel_io.cpp b/toonz/sources/common/timage_io/tlevel_io.cpp new file mode 100644 index 0000000..33e498f --- /dev/null +++ b/toonz/sources/common/timage_io/tlevel_io.cpp @@ -0,0 +1,369 @@ + + +// TnzCore includes +#include "tsystem.h" +#include "tiio.h" +#include "tcontenthistory.h" +#include "tconvert.h" + +// STD includes +#include + +// Qt includes +#include + +#include "tlevel_io.h" + +using namespace std; + +DEFINE_CLASS_CODE(TLevelReader, 8) +DEFINE_CLASS_CODE(TLevelWriter, 9) +//DEFINE_CLASS_CODE(TLevelReaderWriter, 25) //brutto + +//----------------------------------------------------------- + +typedef std::pair LevelReaderKey; +std::map LevelReaderTable; +std::map> LevelWriterTable; +//std::map LevelReaderWriterTable; + +//----------------------------------------------------------- + +TLevelReader::TLevelReader(const TFilePath &path) + : TSmartObject(m_classCode), m_info(0), m_path(path), m_contentHistory(0), m_frameFormat(TFrameId::FOUR_ZEROS) +{ +} + +//----------------------------------------------------------- + +TLevelReader::~TLevelReader() +{ + delete m_contentHistory; + delete m_info; +} + +//----------------------------------------------------------- + +TLevelReaderP::TLevelReaderP(const TFilePath &path, int reader) +{ + QString extension = QString::fromStdString(toLower(path.getType())); + LevelReaderKey key(extension, reader); + std::map::iterator it; + it = LevelReaderTable.find(key); + if (it != LevelReaderTable.end()) { + m_pointer = it->second(path); + assert(m_pointer); + } else { + m_pointer = new TLevelReader(path); + } + m_pointer->addRef(); +} + +//----------------------------------------------------------- + +namespace +{ +bool myLess(const TFilePath &l, const TFilePath &r) +{ + return l.getFrame() < r.getFrame(); +} +} + +//----------------------------------------------------------- + +const TImageInfo *TLevelReader::getImageInfo(TFrameId fid) +{ + if (m_info) + return m_info; + else { + TImageReaderP frameReader = getFrameReader(fid); + if (!frameReader) + return 0; + + const TImageInfo *fInfo = frameReader->getImageInfo(); + if (!fInfo) + return 0; + + m_info = new TImageInfo(*fInfo); + if (m_info->m_properties) + m_info->m_properties = m_info->m_properties->clone(); + + return m_info; + } +} + +//----------------------------------------------------------- + +const TImageInfo *TLevelReader::getImageInfo() +{ + if (m_info) + return m_info; + TLevelP level = loadInfo(); + if (level->getFrameCount() == 0) + return 0; + return getImageInfo(level->begin()->first); +} + +//----------------------------------------------------------- + +TLevelP TLevelReader::loadInfo() +{ + TFilePath parentDir = m_path.getParentDir(); + TFilePath levelName(m_path.getLevelName()); + // cout << "Parent dir = '" << parentDir << "'" << endl; + // cout << "Level name = '" << levelName << "'" << endl; + TFilePathSet files; + try { + files = TSystem::readDirectory(parentDir, false, true, true); + } catch (...) { + throw TImageException(m_path, "unable to read directory content"); + } + TLevelP level; + vector data; + for (TFilePathSet::iterator it = files.begin(); it != files.end(); it++) { + TFilePath ln(it->getLevelName()); + // cout << "try " << *it << " " << it->getLevelName() << endl; + if (levelName == TFilePath(it->getLevelName())) { + try { + level->setFrame(it->getFrame(), TImageP()); + data.push_back(*it); + } catch (string msg) { + throw msg; + } + } + } + if (!data.empty()) { + std::vector::iterator it = std::min_element(data.begin(), data.end(), myLess); + TFilePath fr = (*it).withoutParentDir().withName("").withType(""); + wstring ws = fr.getWideString(); + if (ws.length() == 5) { + if (ws.rfind(L'_') == (int)wstring::npos) + m_frameFormat = TFrameId::FOUR_ZEROS; + else + m_frameFormat = TFrameId::UNDERSCORE_FOUR_ZEROS; + } else { + if (ws.rfind(L'_') == (int)wstring::npos) + m_frameFormat = TFrameId::NO_PAD; + else + m_frameFormat = TFrameId::UNDERSCORE_NO_PAD; + } + + } else + m_frameFormat = TFrameId::FOUR_ZEROS; + + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReader::getFrameReader(TFrameId fid) +{ + return TImageReaderP(m_path.withFrame(fid, m_frameFormat)); +} + +//----------------------------------------------------------- + +void TLevelReader::getSupportedFormats(QStringList &names) +{ + for (std::map::iterator it = LevelReaderTable.begin(); + it != LevelReaderTable.end(); + ++it) { + names.push_back(it->first.first); + } +} + +//----------------------------------------------------------- + +TSoundTrack *TLevelReader::loadSoundTrack() +{ + return 0; +} + +//=========================================================== + +TLevelWriter::TLevelWriter(const TFilePath &path, TPropertyGroup *prop) + : TSmartObject(m_classCode), m_path(path), m_properties(prop), m_contentHistory(0) +{ + string ext = path.getType(); + if (!prop) + m_properties = Tiio::makeWriterProperties(ext); +} + +//----------------------------------------------------------- + +TLevelWriter::~TLevelWriter() +{ + delete m_properties; + delete m_contentHistory; +} + +//----------------------------------------------------------- + +TLevelWriterP::TLevelWriterP(const TFilePath &path, TPropertyGroup *winfo) +{ + QString type = QString::fromStdString(toLower(path.getType())); + std::map>::iterator it; + it = LevelWriterTable.find(type); + if (it != LevelWriterTable.end()) + m_pointer = it->second.first(path, winfo ? winfo->clone() : Tiio::makeWriterProperties(path.getType())); + else + m_pointer = new TLevelWriter(path, winfo ? winfo->clone() : Tiio::makeWriterProperties(path.getType())); + + assert(m_pointer); + m_pointer->addRef(); +} + +//----------------------------------------------------------- + +void TLevelWriter::save(const TLevelP &level) +{ + for (TLevel::Iterator it = level->begin(); it != level->end(); it++) { + if (it->second) + getFrameWriter(it->first)->save(it->second); + } +} + +//----------------------------------------------------------- + +void TLevelWriter::saveSoundTrack(TSoundTrack *) +{ + return; + throw TException("The level format doesn't support soundtracks"); +} + +//----------------------------------------------------------- + +void TLevelWriter::setFrameRate(double fps) +{ + m_frameRate = fps; +} + +//----------------------------------------------------------- + +void TLevelWriter::getSupportedFormats(QStringList &names, bool onlyRenderFormats) +{ + for (std::map>::iterator it = LevelWriterTable.begin(); + it != LevelWriterTable.end(); + ++it) { + if (!onlyRenderFormats || it->second.second) + names.push_back(it->first); + } +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriter::getFrameWriter(TFrameId fid) +{ + TImageWriterP iw(m_path.withFrame(fid)); + iw->setProperties(m_properties); + return iw; +} + +//----------------------------------------------------------- + +void TLevelWriter::setContentHistory(TContentHistory *contentHistory) +{ + if (contentHistory != m_contentHistory) { + delete m_contentHistory; + m_contentHistory = contentHistory; + } +} + +//----------------------------------------------------------- + +void TLevelWriter::renumberFids(const std::map &table) +{ + typedef std::map Table; + + struct locals { + static inline QString qstring(const TFilePath &fp) + { + return QString::fromStdWString(fp.getWideString()); + } + static inline QString temp(const QString &str) + { + return str + QString("_"); + } + }; + + if (m_path.getDots() == "..") { + try { + // Extract all image file paths of the level + QDir parentDir(QString::fromStdWString(m_path.getParentDir().getWideString())); + parentDir.setFilter(QDir::Files); + + QStringList nameFilters( + QString::fromStdWString(m_path.getWideName()) + + ".*." + + QString::fromStdString(m_path.getType())); + parentDir.setNameFilters(nameFilters); + + TFilePathSet fpset; + TSystem::readDirectory(fpset, parentDir, false); // Could throw + + // Traverse each file, trying to match it with a table entry + std::vector storedDstPaths; + + TFilePathSet::iterator st, sEnd(fpset.end()); + for (st = fpset.begin(); st != sEnd; ++st) { + const QString &src = locals::qstring(*st); + const TFrameId &fid = st->getFrame(); // Could throw ! (and I'm quite appalled of that o.o') + + Table::const_iterator dt(table.find(fid)); + if (dt == table.end()) { + // The frame must be removed + QFile::remove(src); + } else { + if (fid == dt->second) + continue; + + // The frame must be renumbered + const QString &dst = locals::qstring(st->withFrame(dt->second)); + + if (!QFile::rename(src, dst)) { + // Use a temporary file rename to ensure that other frames to be renumbered + // are not overwritten. + if (QFile::rename(locals::qstring(*st), locals::temp(dst))) + storedDstPaths.push_back(dst); + + // If the second rename did not happen, the problem was not on dst, but on src. + // Alas, it means that rename on source is not possible - skip. + } + } + } + + // At this point, temporaries should be restored to originals. In case the + // rename of one of those files cannot be finalized, leave the temporary - as + // it may be impossible to roll back (another frame could have been renumbered + // to the would-roll-back frame) ! + + std::vector::iterator dt, dEnd(storedDstPaths.end()); + for (dt = storedDstPaths.begin(); dt != dEnd; ++dt) + QFile::rename(locals::temp(*dt), *dt); + } catch (...) { + // Could not read the directory - skip silently + } + } +} + +//============================================================ + +void TLevelReader::define( + QString extension, + int reader, + TLevelReaderCreateProc *proc) +{ + LevelReaderKey key(extension, reader); + LevelReaderTable[key] = proc; + //cout << "LevelReader " << extension << " registred" << endl; +} + +//----------------------------------------------------------- + +void TLevelWriter::define( + QString extension, + TLevelWriterCreateProc *proc, bool isRenderFormat) +{ + LevelWriterTable[extension] = std::pair(proc, isRenderFormat); + //cout << "LevelWriter " << extension << " registred" << endl; +} diff --git a/toonz/sources/common/tio.cpp b/toonz/sources/common/tio.cpp new file mode 100644 index 0000000..47e6de7 --- /dev/null +++ b/toonz/sources/common/tio.cpp @@ -0,0 +1,3 @@ + + +#include "tio.h" diff --git a/toonz/sources/common/tipc/t32bitsrv_wrap.cpp b/toonz/sources/common/tipc/t32bitsrv_wrap.cpp new file mode 100644 index 0000000..1c83590 --- /dev/null +++ b/toonz/sources/common/tipc/t32bitsrv_wrap.cpp @@ -0,0 +1,83 @@ + + +#include + +#include "t32bitsrv_wrap.h" + +//================================================================================ + +int t32bitsrv::BufferExchanger::read(const char *srcBuf, int len) +{ + m_data = (UCHAR *)memcpy(m_data, srcBuf, len); + return len; +} + +//-------------------------------------------------------------------------------- + +int t32bitsrv::BufferExchanger::write(char *dstBuf, int len) +{ + memcpy(dstBuf, m_data, len); + m_data += len; + + return len; +} + +//================================================================================ + +template +int t32bitsrv::RasterExchanger::read(const char *srcBuf, int len) +{ + if (m_ras->getWrap() == m_ras->getLx()) { + memcpy(m_pix, srcBuf, len); + m_pix = (PIXEL *)((UCHAR *)m_pix + len); + } else { + int xStart = (m_pix - m_ras->pixels(0)) % m_ras->getWrap(); + int remainingData = len; + int lineData = m_ras->getLx() * sizeof(PIXEL); + int lineDataToRead = tmin((int)((m_ras->getLx() - xStart) * sizeof(PIXEL)), remainingData); + + for (; remainingData > 0; + m_pix += (m_ras->getWrap() - xStart), + remainingData -= lineDataToRead, + lineDataToRead = tmin(lineData, remainingData), + xStart = 0) + memcpy(m_pix, srcBuf, lineDataToRead); + } + + return len; +} + +//-------------------------------------------------------------------------------- + +template +int t32bitsrv::RasterExchanger::write(char *dstBuf, int len) +{ + //We pass entire pixels, not just bytes + len = len - (len % sizeof(PIXEL)); + + if (m_ras->getWrap() == m_ras->getLx()) { + memcpy(dstBuf, m_pix, len); + m_pix = (PIXEL *)((UCHAR *)m_pix + len); + } else { + int xStart = (m_pix - m_ras->pixels(0)) % m_ras->getWrap(); + int remainingData = len; + int lineData = m_ras->getLx() * sizeof(PIXEL); + int lineDataToWrite = tmin((int)((m_ras->getLx() - xStart) * sizeof(PIXEL)), remainingData); + + for (; remainingData > 0; + m_pix += (m_ras->getWrap() - xStart), + remainingData -= lineDataToWrite, + lineDataToWrite = tmin(lineData, remainingData), + xStart = 0) + memcpy(dstBuf, m_pix, lineDataToWrite); + } + + return len; +} + +//-------------------------------------------------------------------------------- + +// Explicit specialization of raster exchangers +template class DVAPI t32bitsrv::RasterExchanger; + +//================================================================================ diff --git a/toonz/sources/common/tipc/tipc.cpp b/toonz/sources/common/tipc/tipc.cpp new file mode 100644 index 0000000..3c6f7ec --- /dev/null +++ b/toonz/sources/common/tipc/tipc.cpp @@ -0,0 +1,748 @@ + + +//Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//System-specific includes +#ifdef WIN32 +#include +#elif MACOSX +#include +#include +#endif + +#include "tipc.h" + +/* +PLATFORM-SPECIFIC REMINDERS: + +There are few remarks to be aware when maintaining this code. +Please, be careful that: + +- It seems that, on Windows, QLocalSocket::waitForBytesWritten does not return + success unless the data is actually read on the other end. On Unix this is not + the case, presumably because the data is written to a buffer which can be read by + the other process at some later point. + + Thus, *BE SURE* that every data written is received on the other side. + +- On MACOSX, the default shared memory settings can be quite restrictive. + On a standard machine, the maximum size of a shared segment is 4 MB, exactly + the same as the TOTAL size of shared memory available. + + Whereas tipc respects the former parameter, there must be a way to circumvent the + latter in order to make the allocation of multiple shared segments of the maximum + size. +*/ + +//******************************************************** +// Diagnostics Stuff +//******************************************************** + +//#define TIPC_DEBUG + +#ifdef TIPC_DEBUG +#define tipc_debug(expr) expr +#else +#define tipc_debug(expr) +#endif + +#ifdef TIPC_DEBUG +#include +#endif + +//******************************************************** +// Local namespace Stuff +//******************************************************** + +namespace +{ +int shm_max = -1; +int shm_all = -1; +int shm_seg = -1; +int shm_mni = -1; +} + +//******************************************************** +// tipc Stream Implementation +//******************************************************** + +int tipc::Stream::readSize() +{ + if (m_socket->bytesAvailable() < sizeof(TINT32)) + return -1; + + TINT32 msgSize = -1; + m_socket->peek((char *)&msgSize, sizeof(TINT32)); + + return msgSize; +} + +//------------------------------------------------------------- + +bool tipc::Stream::messageReady() +{ + TINT32 msgSize; + return (msgSize = readSize()) >= 0 && m_socket->bytesAvailable() >= msgSize; +} + +//------------------------------------------------------------- + +bool tipc::Stream::readData(char *data, qint64 dataSize, int msecs) +{ + tipc_debug(qDebug("tipc::Stream::readData entry")); + qint64 r, dataRead = 0; + char *currData = data; + + while (dataRead < dataSize) { + if ((m_socket->bytesAvailable() == 0) && !m_socket->waitForReadyRead(msecs)) { + tipc_debug(qDebug("tipc::Stream::readData exit (unexpected loss of data)")); + return false; + } + + //Read the supplied data + currData += r = m_socket->read(currData, dataSize - dataRead); + dataRead += r; + } + + tipc_debug(qDebug("tipc::Stream::readData exit")); + + return true; +} + +//------------------------------------------------------------- + +bool tipc::Stream::readDataNB(char *data, qint64 dataSize, int msecs, + QEventLoop::ProcessEventsFlag flag) +{ + tipc_debug(qDebug("tipc::Stream::readDataNB entry")); + qint64 r, dataRead = 0; + char *currData = data; + + QEventLoop loop; + QObject::connect(m_socket, SIGNAL(readyRead()), &loop, SLOT(quit())); + QObject::connect(m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)), &loop, SLOT(quit())); + + if (msecs >= 0) + QTimer::singleShot(msecs, &loop, SLOT(quit())); + + while (dataRead < dataSize) { + if (m_socket->bytesAvailable() == 0) { + loop.exec(flag); + if (m_socket->bytesAvailable() == 0) { + tipc_debug(qDebug("tipc::Stream::readDataNB exit (unexpected loss of data)")); + return false; + } + } + + //Read the supplied data + currData += r = m_socket->read(currData, dataSize - dataRead); + dataRead += r; + } + + tipc_debug(qDebug("tipc::Stream::readDataNB exit")); + + return true; +} + +//------------------------------------------------------------- + +/*! + Reads the message and returns its header. + This function reads a complete message from the socket, waiting + until it is completely available. The function accepts + an inactivity timeout which can be supplied to drop the operation + after msecs milliseconds no data has been received. +*/ +bool tipc::Stream::readMessage(Message &msg, int msecs) +{ + TINT32 msgSize = 0; + if (!readData((char *)&msgSize, sizeof(TINT32), msecs)) + return false; + + msg.ba().resize(msgSize); + if (!readData(msg.ba().data(), msgSize, msecs)) + return false; + + return true; +} + +//------------------------------------------------------------- + +/*! + The non-blocking equivalent to readMessage(), this function + performs event processing in a local event loop until all + message data has been received. +*/ +bool tipc::Stream::readMessageNB(Message &msg, int msecs, + QEventLoop::ProcessEventsFlag flag) +{ + TINT32 msgSize = 0; + if (!readDataNB((char *)&msgSize, sizeof(TINT32), msecs, flag)) + return false; + + msg.ba().resize(msgSize); + if (!readDataNB(msg.ba().data(), msgSize, msecs, flag)) + return false; + + return true; +} + +//------------------------------------------------------------- + +/*! + Flushes all data written to the stream. + This function waits until all data written on the stream + has been successfully delivered in output. + Returns true if the operation was successful, false if + it timed out or an error occurred. +*/ +bool tipc::Stream::flush(int msecs) +{ + tipc_debug(qDebug("tipc:flush entry")); + + while (m_socket->bytesToWrite() > 0) { + tipc_debug(qDebug() << "bytes to write:" << m_socket->bytesToWrite()); + bool ok = m_socket->flush(); + tipc_debug(qDebug() << "flush success:" << ok << "bytes to write:" << m_socket->bytesToWrite()); + if (m_socket->bytesToWrite() > 0 && !m_socket->waitForBytesWritten(msecs)) + return false; + } + + tipc_debug(qDebug() << "tipc:flush exit - bytes to write:" << m_socket->bytesToWrite()); + return (m_socket->bytesToWrite() == 0); +} + +//******************************************************** +// tipc Stream Operators +//******************************************************** + +//! \warning This operation assumes that all the message is available for read. +//! Use tipc::stream::readMessage if this cannot be ensured. +tipc::Stream &operator>>(tipc::Stream &stream, tipc::Message &msg) +{ + QLocalSocket *socket = stream.socket(); + msg.clear(); + + TINT32 msgSize; + socket->read((char *)&msgSize, sizeof(TINT32)); + msg.ba().resize(msgSize); + socket->read(msg.ba().data(), msgSize); + return stream; +} + +tipc::Stream &operator<<(tipc::Stream &stream, tipc::Message &msg) +{ + QLocalSocket *socket = stream.socket(); + + TINT32 size = msg.ba().size(); + socket->write((char *)&size, sizeof(TINT32)); + socket->write(msg.ba().data(), size); + + return stream; +} + +//******************************************************** +// tipc Utilities +//******************************************************** + +/*! + Appends the invoking process' pid to the passed srvName. +*/ +QString tipc::applicationSpecificServerName(QString srvName) +{ + return srvName + QString::number(QCoreApplication::applicationPid()); +} + +//------------------------------------------------------------- + +bool tipc::startBackgroundProcess(QString cmdline) +{ +#ifdef WIN32 + QProcess *proc = new QProcess; + proc->start(cmdline); + if (proc->state() == QProcess::NotRunning) { + delete proc; + return false; + } + + QObject::connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), proc, SLOT(deleteLater())); + QObject::connect(proc, SIGNAL(error(QProcess::ProcessError)), proc, SLOT(deleteLater())); + return true; +#else + return QProcess::startDetached(cmdline); + ; +#endif +} + +//------------------------------------------------------------- + +/*! + Invokes the passed command line to run a slave server. + A slave server is hereby intended as a 'child' server process which + automatically destroys itself in case the calling application + crashes. + This process \b MUST support one server, running in the \b MAIN \b THREAD, + whose name is _main. + This function waits until the main server is up and ready to + listen for incoming connections - no timeout accepted. + + \warning Please, observe that a correct slave server name should be + ensured to be unique to the system. +*/ +bool tipc::startSlaveServer(QString srvName, QString cmdline) +{ + if (!tipc::startBackgroundProcess(cmdline)) + return false; + + QString mainSrvName(srvName + "_main"); + + //Establish a dummy socket connection to provide a mean for the process + //to tell whether the calling process exited unexpectedly. + QLocalSocket *dummySock = new QLocalSocket; + dummySock->connectToServer(mainSrvName); + + //Wait up to msecs until the socket is connecting. Wait a small amount of time + //until the server is up and listening to connection (there is no other way to tell). + while (dummySock->state() == QLocalSocket::UnconnectedState) { +#ifdef WIN32 + Sleep(10); +#else + usleep(10 << 10); //10.24 msecs +#endif + + dummySock->connectToServer(mainSrvName); + } + + dummySock->waitForConnected(-1); + + tipc::Stream stream(dummySock); + tipc::Message msg; + + //Supply the 'quit if this socket connection fails' command + //This command ensure termination of the child process in case of some errors + //or ending of the program + stream << (msg << QString("$quit_on_error")); + if (tipc::readMessage(stream, msg, 3000) == QString()) { + std::cout << "tipc::startSlaveServer - tipc::readMessage TIMEOUT" << std::endl; + return false; + } + + //The server should die if dummyDock is destroyed. This should happen when the *MAIN* thread + //in *this process* exits. So, if this is not the main thread, we must move the socket there. + if (QThread::currentThread() != QCoreApplication::instance()->thread()) + dummySock->moveToThread(QCoreApplication::instance()->thread()); + + //If a connection error takes place, release the dummy socket. + //Please, observe that this QObject::connect is invoked *AFTER* the connection trials above... + QObject::connect(dummySock, SIGNAL(error(QLocalSocket::LocalSocketError)), dummySock, SLOT(deleteLater())); + + return true; +} + +//------------------------------------------------------------- + +/*! + Connects the passed socket to the server with name + . + Awaits for the connection up to msecs milliseconds before returning false. + If no server was found, a new slave server is started by invoking + the supplied command line and connection is re-attempted. + Returns true on success, false otherwise. + + \warning Please, observe that a correct slave server name should be + ensured to be unique to the parent process. +*/ +bool tipc::startSlaveConnection(QLocalSocket *socket, QString srvName, int msecs, + QString cmdline, QString threadName) +{ + QTime time; + time.start(); + + if (msecs == -1) + msecs = (std::numeric_limits::max)(); + + QString fullSrvName(srvName + threadName); + socket->connectToServer(fullSrvName); + + //If the socket is not connecting, the server lookup table returned that the no server with + //the passed name exists. This means that a server must be created. + if (socket->state() == QLocalSocket::UnconnectedState && !cmdline.isEmpty()) { + //Completely serialize the server start + static QMutex mutex; + QMutexLocker locker(&mutex); + + //Retry connection - this is required due to the mutex + socket->connectToServer(fullSrvName); + if (socket->state() != QLocalSocket::UnconnectedState) + goto connecting; + + //Invoke the supplied command line to start the server + if (!tipc::startSlaveServer(srvName, cmdline)) + return false; + + //Reconnect to the server + socket->connectToServer(fullSrvName); + if (socket->state() == QLocalSocket::UnconnectedState) + return false; + } + +connecting: + + //Now, the server is connecting or already connected. Wait until the socket is connected. + socket->waitForConnected(msecs - time.elapsed()); + if (socket->state() != QLocalSocket::ConnectedState) + return false; + + return true; +} + +//------------------------------------------------------------- + +/*! + Waits and reads the next message from stream. + This function is mainly a useful macro that encapsulates + the following steps in one call: + + \li Flush the write buffer (output messages) + \li Wait until an input message is completely readable + \li Read the message from stream + \li Read the first string from the message and return it + + This function returns an empty QString if the message could not be + entirely retrieved from the stream. +*/ +QString tipc::readMessage(Stream &stream, Message &msg, int msecs) +{ + msg.clear(); + stream.flush(); + if (!stream.readMessage(msg, msecs)) + return QString(); + + QString res; + msg >> res; + return res; +} + +//------------------------------------------------------------- + +/*! + The non-blocking equivalent to tipc::readMessage. +*/ +QString tipc::readMessageNB(Stream &stream, Message &msg, int msecs, + QEventLoop::ProcessEventsFlag flag) +{ + msg.clear(); + if (!stream.readMessageNB(msg, msecs, flag)) + return QString(); + + QString res; + msg >> res; + return res; +} + +//------------------------------------------------------------- + +/*! + Returns an inter-process unique id string; the returned + id should be used to create QSharedMemory objects. +*/ +QString tipc::uniqueId() +{ + static QAtomicInt count; + count.ref(); + return QString::number(QCoreApplication::applicationPid()) + "_" + QString::number((int)count); +} + +//------------------------------------------------------------- + +//! Returns the maximum size of a shared memory segment allowed by the system +int tipc::shm_maxSegmentSize() +{ + if (shm_max < 0) { +#ifdef MACOSX + //Retrieve it by invoking sysctl + size_t valSize = sizeof(TINT64); + TINT64 val; + sysctlbyname("kern.sysv.shmmax", &val, &valSize, NULL, 0); + shm_max = tmin(val, (TINT64)(std::numeric_limits::max)()); +#else + //Windows case: no such limit + //Observe that QSharedMemory accepts only an int size - so the num_lim is against int. + shm_max = (std::numeric_limits::max)(); +#endif + } + + return shm_max; +} + +//------------------------------------------------------------- + +//! Returns the maximum number of shared segments allowed by the system +int tipc::shm_maxSegmentCount() +{ + if (shm_seg < 0) { +#ifdef MACOSX + size_t valSize = sizeof(TINT64); + TINT64 val; + sysctlbyname("kern.sysv.shmseg", &val, &valSize, NULL, 0); + shm_seg = tmin(val, (TINT64)(std::numeric_limits::max)()); +#else + //Windows case: no such limit - again, using limit against max due to Qt + shm_seg = (std::numeric_limits::max)(); +#endif + } + + return shm_seg; +} + +//------------------------------------------------------------- + +int tipc::shm_maxSharedPages() +{ + if (shm_all < 0) { +#ifdef MACOSX + size_t valSize = sizeof(TINT64); + TINT64 val; + sysctlbyname("kern.sysv.shmall", &val, &valSize, NULL, 0); + shm_all = tmin(val, (TINT64)(std::numeric_limits::max)()); +#else + shm_all = (std::numeric_limits::max)(); +#endif + } + + return shm_all; +} + +//------------------------------------------------------------- + +int tipc::shm_maxSharedCount() +{ + if (shm_mni < 0) { +#ifdef MACOSX + size_t valSize = sizeof(TINT64); + TINT64 val; + sysctlbyname("kern.sysv.shmmni", &val, &valSize, NULL, 0); + shm_mni = tmin(val, (TINT64)(std::numeric_limits::max)()); +#else + shm_mni = (std::numeric_limits::max)(); +#endif + } + + return shm_mni; +} + +//------------------------------------------------------------- + +/*! + Attempts to set the shared memory parameters to the system. + This is only working on MAC's SystemV shm, it's a no-op on Win. + This function will fail anyway if the process is not owned by an + admin. +*/ +void tipc::shm_set(int shmmax, int shmseg, int shmall, int shmmni) +{ + tipc_debug(qDebug("shmmax: %i, shmseg: %i, shmall: %i, shmmni: %i", shmmax, shmseg, shmall, shmmni)); +#ifdef MACOSX + TINT64 val; + int err; + if (shmmax > 0) { + val = shmmax; + err = sysctlbyname("kern.sysv.shmmax", NULL, NULL, &val, sizeof(TINT64)); + if (!err) + shm_max = shmmax; + } + if (shmseg > 0) { + val = shmseg; + err = sysctlbyname("kern.sysv.shmseg", NULL, NULL, &val, sizeof(TINT64)); + if (!err) + shm_seg = shmseg; + } + if (shmall > 0) { + val = shmall; + err = sysctlbyname("kern.sysv.shmall", NULL, NULL, &val, sizeof(TINT64)); + if (!err) + shm_all = shmall; + } + if (shmmni > 0) { + val = shmmni; + err = sysctlbyname("kern.sysv.shmmni", NULL, NULL, &val, sizeof(TINT64)); + if (!err) + shm_mni = shmmni; + } +#endif +} + +//------------------------------------------------------------- + +/*! + Creates a shared memory segment for passed QSharedMemory. + + This function attempts creation of a shared memory segment + in the form of Qt's QSharedMemory, with the following \b UNIX-specific + distinctions: + +
  • If the segment size is beyond that supported by the system, + the function can be set to either fail or return a segment with + the maximum supported size. <\LI> + +
  • Unlike QSharedMemory::create, this function attempts to + reclaim an already existing memory id before creating a new one. <\LI> +*/ +int tipc::create(QSharedMemory &shmem, int size, bool strictSize) +{ + bool ok, retried = false; + + if (!strictSize) + size = tmin(size, (int)shm_maxSegmentSize()); + + tipc_debug(qDebug() << "shMem create: size =" << size); + +retry: + + ok = shmem.create(size); + if (!ok) { + tipc_debug(qDebug() << "Error: Shared Segment could not be created: #" << shmem.errorString()); + + //Unix-specific error recovery follows. See Qt's docs about it. + + //Try to recover error #AlreadyExists - supposedly, the server crashed in a previous instance. + //As shared memory segments that happen to go this way are owned by the server process with 1 + //reference count, detaching it now may solve the issue. + if (shmem.error() == QSharedMemory::AlreadyExists && !retried) { + retried = true; // We're trying this only once... for now it works. + shmem.attach(); + shmem.detach(); + goto retry; + } + + return -1; + } + + return size; +} + +//------------------------------------------------------------- + +/*! + Writes data through a shared memory segment medium. +*/ +bool tipc::writeShMemBuffer(Stream &stream, Message &msg, int bufSize, ShMemWriter *dataWriter) +{ + tipc_debug(QTime time; time.start()); + tipc_debug(qDebug("tipc::writeShMemBuffer entry")); + + static QSemaphore sem(tipc::shm_maxSegmentCount()); + sem.acquire(1); + + { + //Create a shared memory segment, possibly of passed size + QSharedMemory shmem(tipc::uniqueId()); + bool ok = (tipc::create(shmem, bufSize) > 0); + if (!ok) + goto err; + + //Communicate the shared memory id and bufSize to the reader + msg << QString("shm") << shmem.key() << bufSize; + + //Fill in data until all the buffer has been sent + int chunkData, remainingData = bufSize; + while (remainingData > 0) { + //Write to the shared memory segment + tipc_debug(QTime xchTime; xchTime.start()); + shmem.lock(); + remainingData -= chunkData = dataWriter->write((char *)shmem.data(), tmin(shmem.size(), remainingData)); + shmem.unlock(); + tipc_debug(qDebug() << "exchange time:" << xchTime.elapsed()); + + stream << (msg << QString("chk") << chunkData); + + if (tipc::readMessage(stream, msg) != "ok") + goto err; + + msg.clear(); + } + } + + sem.release(1); + tipc_debug(qDebug("tipc::writeShMemBuffer exit")); + tipc_debug(qDebug() << "tipc::writeShMemBuffer time:" << time.elapsed()); + return true; + +err: + + tipc_debug(qDebug("tipc::writeShMemBuffer exit (error)")); + + msg.clear(); + sem.release(1); + return false; +} + +//------------------------------------------------------------- + +/*! + Reads data through a shared memory segment medium. +*/ +bool tipc::readShMemBuffer(Stream &stream, Message &msg, ShMemReader *dataReader) +{ + tipc_debug(QTime time; time.start();); + tipc_debug(qDebug("tipc::readShMemBuffer entry")); + + //Read the id from stream + QString res(tipc::readMessage(stream, msg)); + if (res != "shm") { + tipc_debug(qDebug("tipc::readShMemBuffer exit (res != \"shm\")")); + return false; + } + + //Read message and reply + QString id, chkStr; + int bufSize; + msg >> id >> bufSize >> chkStr; + + //Data is ready to be read - attach to the shared memory segment. + QSharedMemory shmem(id); + shmem.attach(); + if (!shmem.isAttached()) { + tipc_debug(qDebug("tipc::readShMemBuffer exit (shmem not attached)")); + return false; + } + + //Start reading from it + int chunkData, remainingData = bufSize; + while (true) { + msg >> chunkData; + + tipc_debug(QTime xchTime; xchTime.start()); + shmem.lock(); + remainingData -= dataReader->read((const char *)shmem.data(), chunkData); + shmem.unlock(); + tipc_debug(qDebug() << "exchange time:" << xchTime.elapsed()); + + //Data was read. Inform the writer + stream << (msg << clr << QString("ok")); + stream.flush(); + + if (remainingData <= 0) + break; + + //Wait for more chunks + if (tipc::readMessage(stream, msg) != "chk") { + tipc_debug(qDebug("tipc::readShMemBuffer exit (unexpected chunk absence)")); + return false; + } + } + + shmem.detach(); + tipc_debug(qDebug("tipc::readShMemBuffer exit")); + tipc_debug(qDebug() << "tipc::readShMemBuffer time:" << time.elapsed()); + return true; +} diff --git a/toonz/sources/common/tipc/tipcmsg.cpp b/toonz/sources/common/tipc/tipcmsg.cpp new file mode 100644 index 0000000..b1e7bcb --- /dev/null +++ b/toonz/sources/common/tipc/tipcmsg.cpp @@ -0,0 +1,170 @@ + + +//Toonz includes +#include "traster.h" +#include "timage_io.h" + +//Qt includes +#include +#include +#include +#include +#include +#include + +//tipc includes +#include "tipc.h" + +#include "tipcmsg.h" + +//--------------------------------------------------------------------- + +// Local stuff + +namespace +{ +QHash sharedMemories; +QHash temporaryFiles; +} + +//--------------------------------------------------------------------- + +namespace tipc +{ + +//******************************************************************************* +// Shared Memory Request +//******************************************************************************* + +template <> +QString DefaultMessageParser::header() const { return QString("$shmem_request"); } + +//------------------------------------------------------------------ + +template <> +void DefaultMessageParser::operator()(Message &msg) +{ + int size; + QString id; + msg >> id >> size >> clr; + + QSharedMemory *mem = new QSharedMemory(id); + + bool ok = (tipc::create(*mem, size) > 0); + if (!ok) { + msg << QString("err"); + delete mem; + return; + } + + sharedMemories.insert(id, mem); + msg << QString("ok"); +} + +//******************************************************************************* +// Shared Memory Release +//******************************************************************************* + +template <> +QString DefaultMessageParser::header() const { return QString("$shmem_release"); } + +//------------------------------------------------------------------ + +template <> +void DefaultMessageParser::operator()(Message &msg) +{ + QString id; + msg >> id >> clr; + QSharedMemory *mem = sharedMemories.take(id); + if (mem) + delete mem; + msg << QString("ok"); +} + +//******************************************************************************* +// Temporary File Request +//******************************************************************************* + +template <> +QString DefaultMessageParser::header() const { return QString("$tmpfile_request"); } + +//------------------------------------------------------------------ + +template <> +void DefaultMessageParser::operator()(Message &msg) +{ + QString id; + msg >> id >> clr; + + //Build a temporary file with passed id group. + //The created QTemporaryFile CANNOT be stored directly, as it internally + //keeps the file open until the object is destroyed. Instead, we store its + //filePath and manually remove it upon release. + + QTemporaryFile tmp(QDir::temp().filePath(id)); + tmp.setAutoRemove(false); + if (!tmp.open()) { + msg << QString("err"); + return; + } + + temporaryFiles.insert(id, tmp.fileName()); + msg << QString("ok") << tmp.fileName(); +} + +//******************************************************************************* +// Temporary File Release +//******************************************************************************* + +template <> +QString DefaultMessageParser::header() const { return QString("$tmpfile_release"); } + +//------------------------------------------------------------------ + +template <> +void DefaultMessageParser::operator()(Message &msg) +{ + QString id; + msg >> id >> clr; + + QString tmpPath = temporaryFiles.take(id); + if (!tmpPath.isEmpty()) { + QFile file(tmpPath); + file.remove(); + } + + msg << QString("ok"); +} + +//******************************************************************************* +// Quit On Error +//******************************************************************************* + +template <> +QString DefaultMessageParser::header() const { return QString("$quit_on_error"); } + +//------------------------------------------------------------------ + +template <> +void DefaultMessageParser::operator()(Message &msg) +{ + QObject::connect(socket(), SIGNAL(error(QLocalSocket::LocalSocketError)), QCoreApplication::instance(), SLOT(quit())); + //In Qt 5.5 originating process's termination emits 'disconnected' instead of 'error' + QObject::connect(socket(), SIGNAL(disconnected()), QCoreApplication::instance(), SLOT(quit())); + + msg << clr << QString("ok"); +} + +//******************************************************************************* +// Explicit template instantiation for all basic datatypes +//******************************************************************************* + +//-------------------------- Default Messages --------------------------- + +template class DefaultMessageParser; +template class DefaultMessageParser; +template class DefaultMessageParser; +template class DefaultMessageParser; +template class DefaultMessageParser; + +} //namespace tipc diff --git a/toonz/sources/common/tipc/tipcsrv.cpp b/toonz/sources/common/tipc/tipcsrv.cpp new file mode 100644 index 0000000..4c0b52e --- /dev/null +++ b/toonz/sources/common/tipc/tipcsrv.cpp @@ -0,0 +1,167 @@ + + +#include "assert.h" + +//Qt includes +#include +#include + +#include "tipc.h" +#include "tipcmsg.h" + +#include "tipcsrvP.h" +#include "tipcsrv.h" + +//******************************************************************************* +// Diagnostics stuff +//******************************************************************************* + +//#define TIPC_DEBUG + +#ifdef TIPC_DEBUG +#define tipc_debug(expr) expr +#else +#define tipc_debug(expr) +#endif + +#ifdef TIPC_DEBUG +#include +#endif + +//******************************************************************************* +// tipc::SocketListener implementation +//******************************************************************************* + +void tipc::SocketController::onReadyRead() +{ + //Deliver the message to the server for interpretation. + m_server->dispatchSocket(m_socket); +} + +//----------------------------------------------------------------------- + +void tipc::SocketController::onDisconnected() +{ + m_socket->QObject::disconnect(SIGNAL(readyRead())); + + //Auto-delete this + delete this; +} + +//******************************************************************************* +// Server implementation +//******************************************************************************* + +tipc::Server::Server() + : m_lock(false) +{ + connect(this, SIGNAL(newConnection()), this, SLOT(onNewConnection())); + + //Add default parsers + addParser(new DefaultMessageParser); + addParser(new DefaultMessageParser); + addParser(new DefaultMessageParser); + addParser(new DefaultMessageParser); + addParser(new DefaultMessageParser); +} + +//----------------------------------------------------------------------- + +tipc::Server::~Server() +{ + //Release parsers + QHash::iterator it; + for (it = m_parsers.begin(); it != m_parsers.end(); ++it) + delete it.value(); +} + +//----------------------------------------------------------------------- + +void tipc::Server::addParser(MessageParser *parser) +{ + m_parsers.insert(parser->header(), parser); +} + +//----------------------------------------------------------------------- + +void tipc::Server::removeParser(QString header) +{ + MessageParser *parser = m_parsers.take(header); + if (parser) + delete parser; +} + +//----------------------------------------------------------------------- + +void tipc::Server::onNewConnection() +{ + tipc_debug(qDebug("new connection")); + + //Accept the connection + QLocalSocket *socket = nextPendingConnection(); + + //Allocate a controller for the socket + SocketController *controller = new SocketController; + controller->m_server = this; + controller->m_socket = socket; + + //Connect the controller to the socket's signals + connect(socket, SIGNAL(readyRead()), controller, SLOT(onReadyRead())); + connect(socket, SIGNAL(disconnected()), controller, SLOT(onDisconnected())); + connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); + connect(socket, SIGNAL(error(QLocalSocket::LocalSocketError)), + this, SLOT(onError(QLocalSocket::LocalSocketError))); +} + +//----------------------------------------------------------------------- + +void tipc::Server::onError(QLocalSocket::LocalSocketError error) +{ + tipc_debug(qDebug() << "Server error #" << error << ": " << errorString()); +} + +//----------------------------------------------------------------------- + +void tipc::Server::dispatchSocket(QLocalSocket *socket) +{ + //The lock is established when a message is currently being processed. + //Returning if the lock is set avoids having recursive message processing; + //which is possible if a parser expects further message packets. + if (m_lock) + return; + + tipc::Stream stream(socket); + QString header; + + while (socket->bytesAvailable() > 0) { + if (!stream.messageReady()) + return; + + Message msg; + + stream >> msg; + msg >> header; + assert(!header.isEmpty()); + + tipc_debug(qDebug() << header << endl); + + QHash::iterator it = m_parsers.find(header); + if (it == m_parsers.end()) { + tipc_debug(qDebug() << "Error: Unrecognized command" << endl); + continue; + } + + m_lock = true; + + MessageParser *parser = it.value(); + parser->m_socket = socket; + parser->m_stream = &stream; + parser->operator()(msg); + + m_lock = false; + + //The Message has been read and processed. Send the reply. + if (msg.ba().size() > 0) + stream << msg; + } +} diff --git a/toonz/sources/common/tmeshimage/tmeshimage.cpp b/toonz/sources/common/tmeshimage/tmeshimage.cpp new file mode 100644 index 0000000..da809c9 --- /dev/null +++ b/toonz/sources/common/tmeshimage/tmeshimage.cpp @@ -0,0 +1,408 @@ + + +// TnzCore includes +#include "tstream.h" + +// tcg includes +#include "tcg/tcg_misc.h" +#include "tcg/tcg_iterator_ops.h" + +#define INCLUDE_HPP +#include "tmeshimage.h" +#undef INCLUDE_HPP + +//****************************************************************************** +// Explicit instantiations +//****************************************************************************** + +template class DV_EXPORT_API tcg::Vertex; +template class DV_EXPORT_API tcg::Mesh>; +template class DV_EXPORT_API tcg::TriMesh>; + +typedef tcg::TriMesh> TriMesh_base; + +//****************************************************************************** +// TTextureMesh implementation +//****************************************************************************** + +DEFINE_CLASS_CODE(TTextureMesh, 120) +PERSIST_IDENTIFIER(TTextureMesh, "mesh") + +static TTextureMeshP cloneMesh_(const TTextureMeshP &other) +{ + return TTextureMeshP(new TTextureMesh(*other)); +} + +void static_check() +{ + /* input iterator */ + static_assert(std::is_same::iterator>::iterator_category, std::random_access_iterator_tag>::value == true, "random"); + + static_assert(std::is_base_of::iterator>::iterator_category>::value == true, "input"); + + static_assert(std::is_base_of::iterator>::iterator_category>::value == true, "forward"); + + static_assert(std::is_constructible::iterator>::reference>::value == true, + "akan"); + + /* converted iterator */ + std::vector vec; + auto it = vec.end(); + auto c = tcg::make_cast_it(it, cloneMesh_); + + static_assert(std::is_same::iterator_category, std::random_access_iterator_tag>::value == true, "random"); + + static_assert(std::is_base_of::iterator_category>::value == true, "input"); + + static_assert(std::is_base_of::iterator_category>::value == true, "forward"); + + //TTextureMeshP p(std::iterator_traits< decltype(c) >::reference); + static_assert(std::is_constructible::reference>::value == true, + "akan"); +} + +//----------------------------------------------------------------------- + +TTextureMesh::TTextureMesh() + : TSmartObject(m_classCode) +{ +} + +//----------------------------------------------------------------------- + +TTextureMesh::TTextureMesh(const TTextureMesh &other) + : TriMesh_base(other), TSmartObject(m_classCode) +{ +} + +//----------------------------------------------------------------------- + +TTextureMesh &TTextureMesh::operator=(const TTextureMesh &other) +{ + TriMesh_base::operator=(other); + return *this; +} + +//----------------------------------------------------------------------- + +bool TTextureMesh::faceContains(int f, const TPointD &p) const +{ + int v0, v1, v2; + this->faceVertices(f, v0, v1, v2); + + const TPointD &p0 = vertex(v0).P(); + const TPointD &p1 = vertex(v1).P(); + const TPointD &p2 = vertex(v2).P(); + + bool clockwise = (tcg::point_ops::cross(p2 - p0, p1 - p0) >= 0); + return ((tcg::point_ops::cross(p - p0, p1 - p0) >= 0) == clockwise) && + ((tcg::point_ops::cross(p - p1, p2 - p1) >= 0) == clockwise) && + ((tcg::point_ops::cross(p - p2, p0 - p2) >= 0) == clockwise); +} + +//----------------------------------------------------------------------- + +int TTextureMesh::faceContaining(const TPointD &p) const +{ + int f, fCount = facesCount(); + for (f = 0; f < fCount; ++f) + if (faceContains(f, p)) + return f; + return -1; +} + +//----------------------------------------------------------------------- + +TRectD TTextureMesh::getBBox() const +{ + // TODO: Should be cached... + + const double max = (std::numeric_limits::max)(); + TRectD result(max, max, -max, -max); + + // Iterate all meshes + assert(m_vertices.size() == m_vertices.nodesCount()); + + int v, vCount = int(m_vertices.size()); + for (v = 0; v != vCount; ++v) { + const TTextureVertex &vx = m_vertices[v]; + + result.x0 = tmin(result.x0, vx.P().x); + result.y0 = tmin(result.y0, vx.P().y); + result.x1 = tmax(result.x1, vx.P().x); + result.y1 = tmax(result.y1, vx.P().y); + } + + return result; +} + +//----------------------------------------------------------------------- + +void TTextureMesh::saveData(TOStream &os) +{ + struct locals { + static inline bool hasNon1Rigidity(const TTextureMesh &mesh) + { + int v, vCount = int(mesh.verticesCount()); + for (v = 0; v != vCount; ++v) + if (mesh.vertex(v).P().rigidity != 1.0) + return true; + return false; + } + }; + + // NOTE: Primitives saved by INDEX iteration is NOT COINCIDENTAL - since + // the lists' internal linking could have been altered to mismatch the + // natural indexing referred to by primitives' data. + + if (m_vertices.size() != m_vertices.nodesCount() || + m_edges.size() != m_edges.nodesCount() || + m_faces.size() != m_faces.nodesCount()) { + // Ensure the mesh is already squeezed - save a squeezed + // copy if necessary + TTextureMesh mesh(*this); + + mesh.squeeze(); + mesh.saveData(os); + + return; + } + + assert(m_vertices.size() == m_vertices.nodesCount()); + assert(m_edges.size() == m_edges.nodesCount()); + assert(m_faces.size() == m_faces.nodesCount()); + + // Store Vertices + os.openChild("V"); + { + int vCount = int(m_vertices.size()); + os << vCount; + + for (int v = 0; v != vCount; ++v) { + TTextureMesh::vertex_type &vx = m_vertices[v]; + os << vx.P().x << vx.P().y; + } + } + os.closeChild(); + + // Store Edges + os.openChild("E"); + { + int eCount = int(m_edges.size()); + os << eCount; + + for (int e = 0; e != eCount; ++e) { + TTextureMesh::edge_type &ed = m_edges[e]; + os << ed.vertex(0) << ed.vertex(1); + } + } + os.closeChild(); + + // Store Faces + os.openChild("F"); + { + int fCount = int(m_faces.size()); + os << fCount; + + for (int f = 0; f != fCount; ++f) { + TTextureMesh::face_type &fc = m_faces[f]; + + int e, eCount = fc.edgesCount(); + for (e = 0; e < eCount; ++e) + os << fc.edge(e); + } + } + os.closeChild(); + + // Store rigidities + if (locals::hasNon1Rigidity(*this)) { + os.openChild("rigidities"); + { + int vCount = int(m_vertices.size()); + os << vCount; + + for (int v = 0; v != vCount; ++v) + os << m_vertices[v].P().rigidity; + } + os.closeChild(); + } +} + +//----------------------------------------------------------------------- + +void TTextureMesh::loadData(TIStream &is) +{ + typedef tcg::Mesh mesh_type; + + std::string str; + int i, size; + + while (is.openChild(str)) { + if (str == "V") { + is >> size; + + m_vertices.reserve(size); + TTextureMesh::vertex_type v; + + for (i = 0; i < size; ++i) { + is >> v.P().x >> v.P().y; + mesh_type::addVertex(v); + } + + is.closeChild(); + } else if (str == "E") { + is >> size; + + m_edges.reserve(size); + int v0, v1; + + for (i = 0; i < size; ++i) { + is >> v0 >> v1; + mesh_type::addEdge(TTextureMesh::edge_type(v0, v1)); + } + + is.closeChild(); + } else if (str == "F") { + is >> size; + + m_faces.reserve(size); + + int e[3]; + + for (i = 0; i < size; ++i) { + is >> e[0] >> e[1] >> e[2]; + mesh_type::addFace(TTextureMesh::face_type(e)); + } + + is.closeChild(); + } else if (str == "rigidities") { + is >> size; + size = tmin(size, this->verticesCount()); + + for (i = 0; i < size; ++i) + is >> m_vertices[i].P().rigidity; + + is.closeChild(); + } else { + assert(false); + is.skipCurrentTag(); + } + } +} + +//****************************************************************************** +// TMeshImage::Imp definition +//****************************************************************************** + +class TMeshImage::Imp +{ +public: + std::vector m_meshes; //!< Mesh data + double m_dpiX, m_dpiY; //!< Meshes dpi + + Imp() : m_dpiX(), m_dpiY() {} + + Imp(const Imp &other) + : m_meshes(tcg::make_cast_it(other.m_meshes.begin(), cloneMesh), + tcg::make_cast_it(other.m_meshes.end(), cloneMesh)), + m_dpiX(other.m_dpiX), m_dpiY(other.m_dpiY) {} + +private: + static TTextureMeshP cloneMesh(const TTextureMeshP &other) + { + return TTextureMeshP(new TTextureMesh(*other)); + } + + // Not assignable + Imp &operator=(const Imp &other); +}; + +//****************************************************************************** +// TMeshImage implementation +//****************************************************************************** + +TMeshImage::TMeshImage() + : m_imp(new Imp) +{ +} + +//----------------------------------------------------------------------- + +TMeshImage::TMeshImage(Imp *imp) + : m_imp(imp) +{ +} + +//----------------------------------------------------------------------- + +TMeshImage::~TMeshImage() +{ + delete m_imp; +} + +//----------------------------------------------------------------------- + +TMeshImage::TMeshImage(const TMeshImage &other) + : m_imp(new Imp(*other.m_imp)) +{ +} + +//----------------------------------------------------------------------- + +TMeshImage &TMeshImage::operator=(TMeshImage other) +{ + swap(*this, other); + return *this; +} + +//----------------------------------------------------------------------- + +TRectD TMeshImage::getBBox() const +{ + const double max = (std::numeric_limits::max)(); + TRectD result(max, max, -max, -max); + + // Iterate all meshes + int m, mCount = int(m_imp->m_meshes.size()); + for (m = 0; m < mCount; ++m) + result += m_imp->m_meshes[m]->getBBox(); + + return result; +} + +//----------------------------------------------------------------------- + +TImage *TMeshImage::cloneImage() const +{ + return new TMeshImage(*this); +} + +//----------------------------------------------------------------------- + +void TMeshImage::getDpi(double &dpix, double &dpiy) const +{ + dpix = m_imp->m_dpiX, dpiy = m_imp->m_dpiY; +} + +//----------------------------------------------------------------------- + +void TMeshImage::setDpi(double dpix, double dpiy) +{ + m_imp->m_dpiX = dpix, m_imp->m_dpiY = dpiy; +} + +//----------------------------------------------------------------------- + +const std::vector &TMeshImage::meshes() const +{ + return m_imp->m_meshes; +} + +//----------------------------------------------------------------------- + +std::vector &TMeshImage::meshes() +{ + return m_imp->m_meshes; +} diff --git a/toonz/sources/common/tmsgcore.cpp b/toonz/sources/common/tmsgcore.cpp new file mode 100644 index 0000000..888d01a --- /dev/null +++ b/toonz/sources/common/tmsgcore.cpp @@ -0,0 +1,231 @@ + + +#include "tmsgcore.h" +#include +#include +#include +#include + +#include +#include + +TMsgCore *TMsgCore::instance() +{ + static TMsgCore *theInstance = 0; + if (!theInstance) + theInstance = new TMsgCore(); + return theInstance; +} + +//---------------------------------- + +TMsgCore::TMsgCore() + : m_tcpServer(0), m_clientSocket(0) +{ +} + +//---------------------------------- + +void TMsgCore::OnNewConnection() //server side +{ + QTcpSocket *socket; + if (m_tcpServer) + socket = m_tcpServer->nextPendingConnection(); + assert(socket); + + bool ret = connect(socket, SIGNAL(readyRead()), SLOT(OnReadyRead())); + ret = ret && connect(socket, SIGNAL(disconnected()), SLOT(OnDisconnected())); + assert(ret); + m_sockets.insert(socket); +} + +//---------------------------------------------------------- + +#define TMSG_PORT 10545 + +bool TMsgCore::openConnection() //server side +{ + //QHostAddress host = (remoteConnection) ? QHostAddress::AnyQHostAddress::LocalHost : ; + + if (m_tcpServer != 0 && m_tcpServer->serverAddress() == QHostAddress::Any) + return true; + if (m_tcpServer != 0) + delete m_tcpServer; + + m_tcpServer = new QTcpServer(); + bool ret = connect(m_tcpServer, SIGNAL(newConnection()), SLOT(OnNewConnection())); + //bool listen = m_tcpServer->listen(QString("tmsg")+QString::number(QCoreApplication::applicationPid())); + bool listen = m_tcpServer->listen(QHostAddress::Any, TMSG_PORT); //QString("tmsg")+QString::number(QCoreApplication::applicationPid())); + if (!listen) { + QString err = m_tcpServer->errorString(); + } + + assert(ret && listen); + return true; +} + +//--------------------------- + +QString TMsgCore::getConnectionName() //server side +{ + assert(m_tcpServer); + QString serverName = "pippo"; //m_tcpServer->serverName(); + return serverName; +} + +//----------------------------------------------------------------------- + +void TMsgCore::OnDisconnected() //server side +{ + std::set::iterator it = m_sockets.begin(); + while (it != m_sockets.end()) { + if ((*it)->state() != QTcpSocket::ConnectedState) + m_sockets.erase(it++); + else + it++; + } + + //if (m_socketIn) + //delete m_socketIn; + //m_socketIn = 0; +} + +//------------------------------------------------------------------ + +void TMsgCore::OnReadyRead() //server side +{ + std::set::iterator it = m_sockets.begin(); + for (; it != m_sockets.end(); it++) //devo scorrerli tutti perche' non so da quale socket viene il messaggio... + { + if ((*it)->state() == QTcpSocket::ConnectedState && (*it)->bytesAvailable() > 0) + break; + } + if (it != m_sockets.end()) { + readFromSocket(*it); + OnReadyRead(); + } +} + +//------------------------------------------------- + +void TMsgCore::readFromSocket(QTcpSocket *socket) //server side +{ + static char data[1024]; + static QString prevMessage; + QString str; + int byteread; + + while ((byteread = socket->read(data, 1023)) != 0) { + data[byteread] = '\0'; + str += QString(data); + } + QString message = prevMessage + str; + prevMessage = QString(); + if (message.isEmpty()) + return; + + int lastEnd = message.lastIndexOf("#END"); + int lastbegin = message.lastIndexOf("#TMSG"); + if (lastbegin == -1 && lastEnd == -1) { + prevMessage = message; + return; + } else if (lastbegin != -1 && lastEnd != -1 && lastEnd < lastbegin) { + prevMessage = message.right(message.size() - lastbegin); + message.chop(lastbegin); + } + + QStringList messages = message.split("#TMSG", QString::SkipEmptyParts); + + for (int i = 0; i < messages.size(); i++) { + QString str = messages.at(i).simplified(); + str.chop(4); //rimuovo i "#END" alla fine + if (str.startsWith("ERROR")) + DVGui::MsgBox(CRITICAL, str.right(str.size() - 5)); + else if (str.startsWith("WARNING")) + DVGui::MsgBox(WARNING, str.right(str.size() - 7)); + else if (str.startsWith("INFO")) + DVGui::MsgBox(INFORMATION, str.right(str.size() - 4)); + else + assert(false); + } +} + +//------------------------------------------------------ + +TMsgCore::~TMsgCore() +{ + if (m_tcpServer == 0 && m_clientSocket != 0) //client side + { + //m_socketIn->waitForBytesWritten (-1); + // m_clientSocket->disconnectFromServer(); + // m_clientSocket->waitForDisconnected(); + delete m_clientSocket; + m_clientSocket = 0; + } +} + +//---------------------------------- + +bool TMsgCore::send(MsgType type, const QString &message) //client side +{ + if (receivers(SIGNAL(sendMessage(int, const QString &))) == 0) { + if (m_clientSocket == 0 || m_clientSocket->state() != QTcpSocket::ConnectedState) + return false; + + QString socketMessage = (type == CRITICAL ? "#TMSG ERROR " : (type == WARNING ? "#TMSG WARNING " : "#TMSG INFO ")) + message + " #END\n"; + +#if QT_VERSION >= 0x050000 + m_clientSocket->write(socketMessage.toLatin1()); +#else + m_clientSocket->write(socketMessage.toAscii()); +#endif + m_clientSocket->flush(); + //m_clientSocket->waitForBytesWritten (1000); + } else + Q_EMIT sendMessage(type, message); + + return true; +} + +//------------------------------------------------------------------- + +void TMsgCore::connectTo(const QString &address) //client side +{ + m_clientSocket = new QTcpSocket(); + m_clientSocket->connectToHost(address == "" ? QHostAddress::LocalHost : QHostAddress(address), TMSG_PORT); + //m_clientSocket->connectToServer (connectionName, QIODevice::ReadWrite | QIODevice::Text); + bool ret = m_clientSocket->waitForConnected(1000); + if (!ret) { + // std::cout << "cannot connect to "<< address.toStdString() << std::endl; + //std::cout << "error "<< m_clientSocket->errorString().toStdString() << std::endl; + int err = m_clientSocket->error(); + } +} + +//----------------------------------------------------------------------- + +void DVGui::MsgBox(MsgType type, const QString &text) +{ + TMsgCore::instance()->send(type, text); +} + +//----------------------------------------------------------------------- + +void DVGui::error(const QString &msg) +{ + MsgBox(DVGui::CRITICAL, msg); +} + +//----------------------------------------------------------------------------- + +void DVGui::warning(const QString &msg) +{ + MsgBox(DVGui::WARNING, msg); +} + +//----------------------------------------------------------------------------- + +void DVGui::info(const QString &msg) +{ + MsgBox(DVGui::INFORMATION, msg); +} diff --git a/toonz/sources/common/tparam/tcubicbezier.cpp b/toonz/sources/common/tparam/tcubicbezier.cpp new file mode 100644 index 0000000..7486289 --- /dev/null +++ b/toonz/sources/common/tparam/tcubicbezier.cpp @@ -0,0 +1,129 @@ + + +#include "tcubicbezier.h" + +//----------------------------------------------------------------------------- + +DVAPI double invCubicBezierX(double x, + const TPointD &a, + const TPointD &aSpeed, + const TPointD &bSpeed, + const TPointD &b) +{ + double aSpeedX = aSpeed.x; + double bSpeedX = bSpeed.x; + if (aSpeedX == 0) + aSpeedX = epsilon; + if (bSpeedX == 0) + bSpeedX = -epsilon; + + // a*u^3+b*u^2+c*u+d + double x0 = a.x; + double x1 = x0 + aSpeedX; + double x3 = b.x; + double x2 = x3 + bSpeedX; + + double aX = x3 + 3 * (x1 - x2) - x0; + double bX = 3 * (x2 - 2 * x1 + x0); + double cX = 3 * (x1 - x0); + double dX = x0 - x; + + return cubicRoot(aX, bX, cX, dX); +} + +//----------------------------------------------------------------------------- + +DVAPI double getCubicBezierY(double x, + const TPointD &a, + const TPointD &aSpeed, + const TPointD &bSpeed, + const TPointD &b) +{ + double y0 = a.y; + double y1 = y0 + aSpeed.y; + double y3 = b.y; + double y2 = y3 + bSpeed.y; + + double aY = y3 + 3 * (y1 - y2) - y0; + double bY = 3 * (y2 - 2 * y1 + y0); + double cY = 3 * (y1 - y0); + double dY = y0; + + double u = invCubicBezierX(x, a, aSpeed, bSpeed, b); + u = tcrop(u, 0.0, 1.0); + return aY * u * u * u + bY * u * u + cY * u + dY; +} + +//----------------------------------------------------------------------------- + +DVAPI std::pair getMinMaxCubicBezierY(const TPointD &a, + const TPointD &aSpeed, + const TPointD &bSpeed, + const TPointD &b) +{ + double y0 = a.y; + double y1 = y0 + aSpeed.y; + double y3 = b.y; + double y2 = y3 + bSpeed.y; + + double aY = y3 + 3 * (y1 - y2) - y0; + double bY = 3 * (y2 - 2 * y1 + y0); + double cY = 3 * (y1 - y0); + double dY = y0; + + double x0 = a.x; + double x1 = x0 + aSpeed.x; + double x3 = b.x; + double x2 = x3 + bSpeed.x; + + double aX = x3 + 3 * (x1 - x2) - x0; + double bX = 3 * (x2 - 2 * x1 + x0); + double cX = 3 * (x1 - x0); + double dX = x0; + + double aaY = 3 * (y1 - y2) - y0 + y3; + double bbY = 2 * (y0 + y2 - 2 * y1); + double ccY = y1 - y0; + + if (aaY == 0) { + if (a.y < b.y) + return std::pair(TPointD(a.x, a.y), + TPointD(b.x, b.y)); + else + return pair(TPointD(b.x, b.y), + TPointD(a.x, a.y)); + } else { + double discr = bbY * bbY - 4 * aaY * ccY; + if (discr < 0) { + if (a.y < b.y) + return std::pair(TPointD(a.x, a.y), + TPointD(b.x, b.y)); + else + return pair(TPointD(b.x, b.y), + TPointD(a.x, a.y)); + } else { + double sqrt_discr = sqrt(discr); + double inv_2aY = 1.0 / (2.0 * aaY); + double u0 = (-bbY + sqrt_discr) * inv_2aY; + double u1 = (-bbY - sqrt_discr) * inv_2aY; + if (u0 > 1.0) + u0 = 1.0; + if (u0 < 0.0) + u0 = 0.0; + if (u1 > 1.0) + u1 = 1.0; + if (u1 < 0.0) + u1 = 0.0; + double y_0 = aY * u0 * u0 * u0 + bY * u0 * u0 + cY * u0 + dY; + double y_1 = aY * u1 * u1 * u1 + bY * u1 * u1 + cY * u1 + dY; + double x_0 = aX * u0 * u0 * u0 + bX * u0 * u0 + cX * u0 + dX; + double x_1 = aX * u1 * u1 * u1 + bX * u1 * u1 + cX * u1 + dX; + if (y_0 < y_1) + return pair(TPointD(x_0, y_0), TPointD(x_1, y_1)); + else + return pair(TPointD(x_1, y_1), TPointD(x_0, y_0)); + } + } +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tparam/tdoublekeyframe.cpp b/toonz/sources/common/tparam/tdoublekeyframe.cpp new file mode 100644 index 0000000..c3396b0 --- /dev/null +++ b/toonz/sources/common/tparam/tdoublekeyframe.cpp @@ -0,0 +1,154 @@ + + +#include "tdoublekeyframe.h" +#include "tstream.h" +#include "tconvert.h" +#include "tunit.h" + +TDoubleKeyframe::TDoubleKeyframe(double frame, double value) + : m_type(Linear), m_frame(frame), m_value(value), m_step(1), m_isKeyframe(false), m_speedIn(), m_speedOut(), m_linkedHandles(true), m_expressionText(""), m_fileParams(), m_similarShapeOffset(0), m_unitName("") +{ +} + +TDoubleKeyframe::~TDoubleKeyframe() +{ +} + +void TDoubleKeyframe::saveData(TOStream &os) const +{ + static std::map typeCodes; + if (typeCodes.empty()) { + typeCodes[None] = "n"; + typeCodes[Constant] = "C"; + typeCodes[Linear] = "L"; + typeCodes[Exponential] = "Exp"; + typeCodes[SpeedInOut] = "S"; + typeCodes[EaseInOut] = "E"; + typeCodes[EaseInOutPercentage] = "Ep"; + typeCodes[Expression] = "Ex"; + typeCodes[File] = "F"; + typeCodes[SimilarShape] = "SimShape"; + }; + map attr; + if (!m_linkedHandles) + attr["lnk"] = "no"; + if (m_step > 1) + attr["step"] = toString(m_step); + os.openChild(typeCodes[m_type], attr); + switch (m_prevType) { + case Linear: + os.child("prev") << m_value; + break; + case SpeedInOut: + os.child("prev") << m_value << m_speedIn.x << m_speedIn.y; + break; + case EaseInOut: + case EaseInOutPercentage: + os.child("prev") << m_value << m_speedIn.x; + break; + } + string unitName = m_unitName != "" ? m_unitName : "default"; + switch (m_type) { + case Constant: + case Exponential: + case Linear: + os << m_frame << m_value; + break; + case SpeedInOut: + os << m_frame << m_value + << m_speedOut.x << m_speedOut.y; + break; + case EaseInOut: + case EaseInOutPercentage: + os << m_frame << m_value + << m_speedOut.x; + break; + case Expression: + os << m_frame << m_expressionText << unitName; + break; + case SimilarShape: + os << m_frame << m_value << m_expressionText << m_similarShapeOffset; + break; + case File: + os << m_frame << m_fileParams.m_path << m_fileParams.m_fieldIndex << unitName; + break; + } + os.closeChild(); +} + +void TDoubleKeyframe::loadData(TIStream &is) +{ + static std::map typeCodes; + if (typeCodes.empty()) { + typeCodes["n"] = None; + typeCodes["C"] = Constant; + typeCodes["L"] = Linear; + typeCodes["Exp"] = Exponential; + typeCodes["S"] = SpeedInOut; + typeCodes["E"] = EaseInOut; + typeCodes["Ep"] = EaseInOutPercentage; + typeCodes["Ex"] = Expression; + typeCodes["F"] = File; + typeCodes["SimShape"] = SimilarShape; + }; + + string tagName; + if (!is.matchTag(tagName)) + return; + std::map::iterator it = + typeCodes.find(tagName); + if (it == typeCodes.end()) { + throw TException(tagName + " : unexpected tag"); + } + m_type = it->second; + is.getTagParam("step", m_step); + string lnkValue; + if (is.getTagParam("lnk", lnkValue) && lnkValue == "no") + m_linkedHandles = false; + if (is.matchTag(tagName)) { + if (tagName != "prev") + throw TException(tagName + " : unexpected tag"); + + is >> m_value; + if (!is.eos()) { + is >> m_speedIn.x; + if (!is.eos()) + is >> m_speedIn.y; + } + if (!is.matchEndTag()) + throw TException(tagName + " : missing endtag"); + } + double dummy0, dummy1; + switch (m_type) { + case Constant: + case Linear: + case Exponential: + is >> m_frame >> m_value; + break; + case SpeedInOut: + is >> m_frame >> m_value >> m_speedOut.x >> m_speedOut.y; + if (!is.eos()) + is >> dummy0 >> dummy1; // old and wrong format. used only during the 6.0 release + break; + case EaseInOut: + case EaseInOutPercentage: + is >> m_frame >> m_value >> m_speedOut.x; + if (!is.eos()) + is >> dummy0; // old and wrong format. used only during the 6.0 release + break; + case Expression: + is >> m_frame >> m_expressionText >> m_unitName; + break; + case SimilarShape: + is >> m_frame >> m_value >> m_expressionText >> m_similarShapeOffset; + break; + case File: + is >> m_frame >> m_fileParams.m_path >> m_fileParams.m_fieldIndex >> m_unitName; + break; + } + if (!is.matchEndTag()) + throw TException(tagName + " : missing endtag"); + if (m_unitName == "default") + m_unitName = ""; + m_isKeyframe = true; +} diff --git a/toonz/sources/common/tparam/tdoubleparam.cpp b/toonz/sources/common/tparam/tdoubleparam.cpp new file mode 100644 index 0000000..ca3770c --- /dev/null +++ b/toonz/sources/common/tparam/tdoubleparam.cpp @@ -0,0 +1,1328 @@ + + +// TnzCore includes +#include "tstream.h" +#include "tconvert.h" +#include "tsystem.h" +#include "tcubicbezier.h" +#include "tundo.h" + +// TnzBase includes +#include "tdoublekeyframe.h" +#include "tdoubleparamfile.h" +#include "texpression.h" +#include "tgrammar.h" +#include "tparser.h" +#include "tunit.h" + +// STD includes +#include + +#include "tdoubleparam.h" + +using namespace std; + +//=============================== + +class TActualDoubleKeyframe : public TDoubleKeyframe +{ +public: + mutable TExpression m_expression; + mutable TDoubleParamFileData m_fileData; + + TActualDoubleKeyframe(double frame = 0, double value = 0) + : TDoubleKeyframe(frame, value), m_unit(0) {} + explicit TActualDoubleKeyframe(const TDoubleKeyframe &src) : m_unit(0) + { + TDoubleKeyframe::operator=(src); + if (m_type == Expression || m_type == SimilarShape) + m_expression.setText(m_expressionText); + else if (m_type == File) + m_fileData.setParams(m_fileParams); + } + TActualDoubleKeyframe &operator=(const TDoubleKeyframe &src) + { + + TDoubleKeyframe::operator=(src); + m_unit = 0; + if (m_type == Expression || m_type == SimilarShape) { + m_expression.setText(m_expressionText); + } else if (m_type == File) { + m_fileData.setParams(m_fileParams); + } + return *this; + } + + const TUnit *updateUnit(const TMeasure *measure) + { + if (!measure) { + m_unit = 0; + m_unitName = ""; + } else { + if (m_unitName != "") + m_unit = measure->getUnit(toWideString(m_unitName)); + else + m_unit = 0; + if (!m_unit) { + m_unit = measure->getCurrentUnit(); + if (m_unit) { + QString app = QString::fromStdWString(m_unit->getDefaultExtension()); + m_unitName = app.toStdString(); + } + } + } + assert(measure || m_unit == 0 && m_unitName == ""); + assert((m_unit == 0) == (m_unitName == "")); + QString app = QString::fromStdString(m_unitName); + assert(m_unit == 0 || m_unit->isExtension(app.toStdWString())); + return m_unit; + } + + double convertFrom(TMeasure *measure, double value) const + { + if (!m_unit) + const_cast(this)->updateUnit(measure); + if (m_unit) + value = m_unit->convertFrom(value); + return value; + } + +private: + mutable const TUnit *m_unit; +}; + +typedef vector DoubleKeyframeVector; + +//=================================================================== + +inline double getConstantValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + double f) +{ + return (f == k1.m_frame) ? k1.m_value : k0.m_value; +} + +//--------------------------------------------------------- + +inline double getLinearValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + double f) +{ + return k0.m_value + + (f - k0.m_frame) * (k1.m_value - k0.m_value) / (k1.m_frame - k0.m_frame); +} + +//--------------------------------------------------------- + +void truncateSpeeds(double aFrame, double bFrame, TPointD &aSpeedTrunc, TPointD &bSpeedTrunc) +{ + double deltaX = bFrame - aFrame; + if (aSpeedTrunc.x < 0) + aSpeedTrunc.x = 0; + if (bSpeedTrunc.x > 0) + bSpeedTrunc.x = 0; + + if (aFrame + aSpeedTrunc.x > bFrame) { + if (aSpeedTrunc.x != 0) { + aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x); + } + } + + if (bFrame + bSpeedTrunc.x < aFrame) { + if (bSpeedTrunc.x != 0) { + bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x); + } + } +} + +//--------------------------------------------------------- + +inline double getSpeedInOutValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + const TPointD &speed0, + const TPointD &speed1, + double frame) +{ + + double aFrame = k0.m_frame; + double bFrame = k1.m_frame; + + double aValue = k0.m_value; + double bValue = k1.m_value; + + if (frame <= aFrame) + return aValue; + else if (frame >= bFrame) + return bValue; + + TPointD aSpeedTrunc = speed0; + TPointD bSpeedTrunc = speed1; + truncateSpeeds(aFrame, bFrame, aSpeedTrunc, bSpeedTrunc); + + return getCubicBezierY(frame, + TPointD(aFrame, aValue), + aSpeedTrunc, + bSpeedTrunc, + TPointD(bFrame, bValue)); +} + +//--------------------------------------------------------- + +DV_EXPORT_API void splitSpeedInOutSegment( + TDoubleKeyframe &k, + TDoubleKeyframe &k0, + TDoubleKeyframe &k1) +{ + if (k.m_frame <= k0.m_frame) { + k = k0; + return; + } else if (k.m_frame >= k1.m_frame) { + k = k1; + return; + } + + TPointD aSpeed = k0.m_speedOut; + TPointD bSpeed = k1.m_speedIn; + truncateSpeeds(k0.m_frame, k1.m_frame, aSpeed, bSpeed); + + TPointD p0(k0.m_frame, k0.m_value); + TPointD p3(k1.m_frame, k1.m_value); + TPointD p1 = p0 + aSpeed; + TPointD p2 = p3 + bSpeed; + double t = invCubicBezierX(k.m_frame, p0, aSpeed, bSpeed, p3); + t = tcrop(t, 0.0, 1.0); + + TPointD p01 = (1 - t) * p0 + t * p1; + TPointD p12 = (1 - t) * p1 + t * p2; + TPointD p23 = (1 - t) * p2 + t * p3; + + TPointD p012 = (1 - t) * p01 + t * p12; + TPointD p123 = (1 - t) * p12 + t * p23; + + TPointD p = (1 - t) * p012 + t * p123; + + assert(fabs(p.x - k.m_frame) < 0.1e-3); + k.m_value = p.y; + + k0.m_speedOut = p01 - p0; + k.m_speedIn = p012 - p; + k.m_speedOut = p123 - p; + k1.m_speedIn = p23 - p3; +} + +//--------------------------------------------------------- + +inline double getEaseInOutValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + double frame, + bool percentage) +{ + double x3 = k1.m_frame - k0.m_frame; + if (x3 <= 0.0) + return k0.m_value; + double x = frame - k0.m_frame; + if (x <= 0) + return k0.m_value; + else if (x >= x3) + return k1.m_value; + double e0 = tmax(k0.m_speedOut.x, 0.0); + double e1 = tmax(-k1.m_speedIn.x, 0.0); + if (percentage) { + e0 *= x3 * 0.01; + e1 *= x3 * 0.01; + } + if (e0 + e1 >= x3) { + double x = tcrop((e0 + x3 - e1) / 2, 0.0, x3); + e0 = x; + e1 = x3 - x; + } + double x1 = e0, x2 = x3 - e1; + if (0 < x1 - x2 && x1 - x2 < 0.1e-5) + x1 = x2 = (x1 + x2) * 0.5; // against rounding problems + assert(0 <= x1 && x1 <= x2 && x2 <= x3); + double v = 2 / (x3 + x2 - x1); + double value = 0; + if (x < x1) { + double a = v / e0; + value = 0.5 * a * x * x; + } else if (x > x2) { + double a = v / e1; + value = 1 - 0.5 * a * (x3 - x) * (x3 - x); + } else { + double c = -0.5 * v * e0; + value = x * v + c; + } + return (1 - value) * k0.m_value + value * k1.m_value; +} + +//--------------------------------------------------------- + +inline double getExponentialValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + double frame) +{ + double aFrame = k0.m_frame; + double bFrame = k1.m_frame; + double deltaX = bFrame - aFrame; + + double aValue = k0.m_value; + double bValue = k1.m_value; + + // if min(aValue,bValue)<=0 => error => linear + if (aValue <= 0 || bValue <= 0) + return getLinearValue(k0, k1, frame); + + double t = (frame - aFrame) / deltaX; + // if aValue k0.m_frame) + t = rframe / (k1.m_frame - k0.m_frame); + TSyntax::Calculator *calculator = k0.m_expression.getCalculator(); + if (calculator) { + calculator->setUnit((const_cast(k0)).updateUnit(measure)); + return calculator->compute(t, frame + 1, rframe + 1); + } else if (measure) + return measure->getDefaultValue(); + else + return 0; +} + +//--------------------------------------------------------- + +inline double getSimilarShapeValue( + const TActualDoubleKeyframe &k0, + const TActualDoubleKeyframe &k1, + double frame, + const TMeasure *measure) +{ + double offset = k0.m_similarShapeOffset; + double rv0 = getExpressionValue(k0, k1, k0.m_frame + offset, measure); + double rv1 = getExpressionValue(k0, k1, k1.m_frame + offset, measure); + double rv = getExpressionValue(k0, k1, frame + offset, measure); + double v0 = k0.m_value; + double v1 = k1.m_value; + if (rv1 != rv0) + return v0 + (v1 - v0) * (rv - rv0) / (rv1 - rv0); + else if (measure) + return measure->getDefaultValue(); + else + return 0; +} + +//=================================================================== + +class TDoubleParam::Imp +{ +public: + const TSyntax::Grammar *m_grammar; + string m_measureName; + TMeasure *m_measure; + double m_defaultValue, m_minValue, m_maxValue; + DoubleKeyframeVector m_keyframes; + bool m_cycleEnabled; + + std::set m_observers; + + Imp(double v = 0.0) + : m_grammar(0), m_measureName(), m_measure(0), m_defaultValue(v), m_minValue(-(std::numeric_limits::max)()), m_maxValue((std::numeric_limits::max)()), m_cycleEnabled(false) + { + } + + ~Imp() {} + + void copy(Imp *src) + { + m_grammar = src->m_grammar; + m_measureName = src->m_measureName; + m_measure = src->m_measure; + m_defaultValue = src->m_defaultValue; + m_minValue = src->m_minValue; + m_maxValue = src->m_maxValue; + m_keyframes = src->m_keyframes; + m_cycleEnabled = src->m_cycleEnabled; + } + + void notify(const TParamChange &change) + { + std::set::iterator it = m_observers.begin(); + for (; it != m_observers.end(); ++it) + (*it)->onChange(change); + } + + double getValue(int segmentIndex, double frame); + double getSpeed(int segmentIndex, double frame); + TPointD getSpeedIn(int kIndex); + TPointD getSpeedOut(int kIndex); +}; + +//--------------------------------------------------------- + +double TDoubleParam::Imp::getValue(int segmentIndex, double frame) +{ + assert(0 <= segmentIndex && segmentIndex + 1 < (int)m_keyframes.size()); + const TActualDoubleKeyframe &k0 = m_keyframes[segmentIndex]; + const TActualDoubleKeyframe &k1 = m_keyframes[segmentIndex + 1]; + + double value = m_defaultValue; + bool convertUnit = false; + switch (k0.m_type) { + case TDoubleKeyframe::Constant: + value = getConstantValue(k0, k1, frame); + break; + case TDoubleKeyframe::Linear: + value = getLinearValue(k0, k1, frame); + break; + case TDoubleKeyframe::SpeedInOut: + value = getSpeedInOutValue(k0, k1, getSpeedOut(segmentIndex), getSpeedIn(segmentIndex + 1), frame); + break; + case TDoubleKeyframe::EaseInOut: + value = getEaseInOutValue(k0, k1, frame, false); + break; + case TDoubleKeyframe::EaseInOutPercentage: + value = getEaseInOutValue(k0, k1, frame, true); + break; + case TDoubleKeyframe::Exponential: + value = getExponentialValue(k0, k1, frame); + break; + case TDoubleKeyframe::Expression: + value = getExpressionValue(k0, k1, frame, m_measure); + convertUnit = true; + break; + case TDoubleKeyframe::File: + value = k0.m_fileData.getValue(frame, m_defaultValue); + convertUnit = true; + break; + case TDoubleKeyframe::SimilarShape: + value = getSimilarShapeValue(k0, k1, frame, m_measure); + break; + } + if (convertUnit) + value = k0.convertFrom(m_measure, value); + return value; +} + +//--------------------------------------------------------- + +double TDoubleParam::Imp::getSpeed(int segmentIndex, double frame) +{ + const double h = 0.00001; + return (getValue(segmentIndex, frame + h) - getValue(segmentIndex, frame - h)) / (2 * h); +} + +//--------------------------------------------------------- + +TPointD TDoubleParam::Imp::getSpeedIn(int kIndex) +{ + assert(1 <= kIndex && kIndex < (int)m_keyframes.size()); + const TActualDoubleKeyframe &kf0 = m_keyframes[kIndex - 1]; + const TActualDoubleKeyframe &kf1 = m_keyframes[kIndex]; + assert(kf0.m_type == TDoubleKeyframe::SpeedInOut); + if (!kf1.m_linkedHandles) + return kf1.m_speedIn; + TPointD speedIn = kf1.m_speedIn; + if (kIndex + 1 >= (int)m_keyframes.size()) { + // speedIn.y = 0; + } else { + if (kf1.m_type != TDoubleKeyframe::SpeedInOut && + (kf1.m_type != TDoubleKeyframe::Expression || !kf1.m_expression.isCycling())) { + double speed = getSpeed(kIndex, kf1.m_frame); + speedIn.y = speedIn.x * speed; + } + } + return speedIn; +} + +//--------------------------------------------------------- + +TPointD TDoubleParam::Imp::getSpeedOut(int kIndex) +{ + assert(0 <= kIndex && kIndex < (int)m_keyframes.size()); + const TDoubleKeyframe &kf1 = m_keyframes[kIndex]; + assert(kf1.m_type == TDoubleKeyframe::SpeedInOut); + if (!kf1.m_linkedHandles) + return kf1.m_speedOut; + TPointD speedOut = kf1.m_speedOut; + if (kIndex == 0) { + // speedOut.y = 0; + } else { + const TDoubleKeyframe &kf0 = m_keyframes[kIndex - 1]; + if (kf0.m_type != TDoubleKeyframe::SpeedInOut) { + double speed = getSpeed(kIndex - 1, kf1.m_frame); + speedOut.y = speedOut.x * speed; + } + } + return speedOut; +} + +//--------------------------------------------------------- + +TPointD TDoubleParam::getSpeedIn(int kIndex) const { return m_imp->getSpeedIn(kIndex); } +TPointD TDoubleParam::getSpeedOut(int kIndex) const { return m_imp->getSpeedOut(kIndex); } + +//========================================================= + +PERSIST_IDENTIFIER(TDoubleParam, "doubleParam") + +//--------------------------------------------------------- + +TDoubleParam::TDoubleParam(double v) + : m_imp(new TDoubleParam::Imp(v)) +{ +} + +//--------------------------------------------------------- + +TDoubleParam::TDoubleParam(const TDoubleParam &src) + : TParam(src.getName()), m_imp(new TDoubleParam::Imp()) +{ + m_imp->copy(src.m_imp); +} + +//--------------------------------------------------------- + +TDoubleParam::~TDoubleParam() +{ + delete m_imp; +} + +//--------------------------------------------------------- + +TDoubleParam &TDoubleParam::operator=(const TDoubleParam &dp) +{ + setName(dp.getName()); + m_imp->copy(dp.m_imp); + return *this; +} + +//--------------------------------------------------------- + +void TDoubleParam::copy(TParam *src) +{ + TDoubleParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_imp->copy(p->m_imp); + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); +} + +//--------------------------------------------------------- + +void TDoubleParam::setValueRange(double min, double max, double step) +{ + if (min > max) + min = max; + + m_imp->m_minValue = min; + m_imp->m_maxValue = max; + // m_imp->m_valueStep = step; +} + +//--------------------------------------------------------- + +bool TDoubleParam::getValueRange(double &min, double &max, double &step) const +{ + min = m_imp->m_minValue; + max = m_imp->m_maxValue; + step = 1; // m_imp->m_valueStep; + return min < max; +} + +//--------------------------------------------------------- + +double TDoubleParam::getDefaultValue() const +{ + assert(m_imp); + return m_imp->m_defaultValue; +} + +//--------------------------------------------------------- + +void TDoubleParam::enableCycle(bool enabled) +{ + m_imp->m_cycleEnabled = enabled; + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); +} + +//--------------------------------------------------------- + +bool TDoubleParam::isCycleEnabled() const +{ + return m_imp->m_cycleEnabled; +} + +//========================================================= + +//========================================================= + +double TDoubleParam::getValue(double frame, bool leftmost) const +{ + double value = 0; + assert(m_imp); + const DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + if (keyframes.empty()) { + // no keyframes: return the default value + value = m_imp->m_defaultValue; + } else if (keyframes.size() == 1) { + // a single keyframe. Type must be keyframe based (no expression/file) + value = keyframes[0].m_value; + } else { + // keyframes range is [f0,f1] + double f0 = keyframes.begin()->m_frame; + double f1 = keyframes.back().m_frame; + if (frame < f0) + frame = f0; + else if (frame > f1 && !m_imp->m_cycleEnabled) + frame = f1; + double valueOffset = 0; + + if (m_imp->m_cycleEnabled) { + double dist = (f1 - f0); + double dvalue = keyframes.back().m_value - keyframes.begin()->m_value; + while (frame >= f1) { + if (frame != f1 || !leftmost) { + frame -= dist; + valueOffset += dvalue; + } else + break; + } + } + + // frame is in [f0,f1] + assert(f0 <= frame && frame <= f1); + + DoubleKeyframeVector::const_iterator b; + b = std::lower_bound(keyframes.begin(), keyframes.end(), TDoubleKeyframe(frame)); + assert(b != keyframes.end()); + DoubleKeyframeVector::const_iterator a; + if (b->m_frame == frame && (b + 1) != keyframes.end()) { + a = b; + b++; + } else { + assert(b != keyframes.begin()); + a = b - 1; + } + + if (leftmost && frame - a->m_frame < 0.00001 && a != keyframes.begin()) { + a--; + b--; + } + + // segment (a,b) contains frame + assert(a != keyframes.end()); + assert(b != keyframes.end()); + assert(a->m_frame <= frame); + assert(b->m_frame >= frame); + + int kIndex = std::distance(keyframes.begin(), a); + + vector tmpKeyframe(3); + + // if segment is keyframe based .... + if (TDoubleKeyframe::isKeyframeBased(a->m_type)) { + // .. and next segment is not then update the b value + if ((b + 1) != keyframes.end() && !TDoubleKeyframe::isKeyframeBased(b->m_type)) { + tmpKeyframe[0] = *b; + if (b->m_type != TDoubleKeyframe::Expression || !b->m_expression.isCycling()) + tmpKeyframe[0].m_value = getValue(b->m_frame); + b = tmpKeyframe.begin(); + } + // .. and/or if prev segment is not then update the a value + if (a != keyframes.begin() && !TDoubleKeyframe::isKeyframeBased(a[-1].m_type)) { + tmpKeyframe[1] = *a; + tmpKeyframe[1].m_value = getValue(a->m_frame, true); + a = tmpKeyframe.begin() + 1; + } + } + + if (a->m_step > 1) { + tmpKeyframe[2] = *b; + b = tmpKeyframe.begin() + 2; + + int relPos = tfloor(b->m_frame - a->m_frame), step = tmin(a->m_step, relPos); + + tmpKeyframe[2].m_frame = a->m_frame + tfloor(relPos, step); + if (frame > b->m_frame) + frame = b->m_frame; + + frame = a->m_frame + tfloor(tfloor(frame - a->m_frame), step); + } + + assert(0 <= kIndex && kIndex + 1 < (int)m_imp->m_keyframes.size()); + value = m_imp->m_defaultValue; + bool convertUnit = false; + switch (a->m_type) { + case TDoubleKeyframe::Constant: + value = getConstantValue(*a, *b, frame); + break; + case TDoubleKeyframe::Linear: + value = getLinearValue(*a, *b, frame); + break; + case TDoubleKeyframe::SpeedInOut: + value = getSpeedInOutValue(*a, *b, getSpeedOut(kIndex), getSpeedIn(kIndex + 1), frame); + break; + case TDoubleKeyframe::EaseInOut: + value = getEaseInOutValue(*a, *b, frame, false); + break; + case TDoubleKeyframe::EaseInOutPercentage: + value = getEaseInOutValue(*a, *b, frame, true); + break; + case TDoubleKeyframe::Exponential: + value = getExponentialValue(*a, *b, frame); + break; + case TDoubleKeyframe::Expression: + value = getExpressionValue(*a, *b, frame, m_imp->m_measure); + convertUnit = true; + break; + case TDoubleKeyframe::File: + value = a->m_fileData.getValue(frame, m_imp->m_defaultValue); + convertUnit = true; + break; + case TDoubleKeyframe::SimilarShape: + value = getSimilarShapeValue(*a, *b, frame, m_imp->m_measure); + // convertUnit = true; + break; + + default: + value = 0.0; + } + value += valueOffset; + if (convertUnit) + value = a->convertFrom(m_imp->m_measure, value); + } + + //if (cropped) + // value = tcrop(value, m_imp->m_minValue, m_imp->m_maxValue); + return value; +} + +//--------------------------------------------------------- + +bool TDoubleParam::setValue(double frame, double value) +{ + assert(m_imp); + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + DoubleKeyframeVector::iterator it; + TActualDoubleKeyframe k(frame); + it = std::lower_bound(keyframes.begin(), keyframes.end(), k); + int index = 0; + bool created = false; + /*-- キーフレームが見つかった場合 --*/ + if (it != keyframes.end() && it->m_frame == frame) { + // changing a keyframe value + index = std::distance(keyframes.begin(), it); + TActualDoubleKeyframe oldKeyframe = *it; + if (oldKeyframe.m_type == TDoubleKeyframe::Expression || + oldKeyframe.m_type == TDoubleKeyframe::File) + return false; + + it->m_value = value; + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + } + /*-- セグメントの部分なので、新たにキーフレームを作る --*/ + else { + assert(it == keyframes.end() || it->m_frame > frame); + + // can't change value in a file/expression segment + if (it != keyframes.end() && it > keyframes.begin() && + ((it - 1)->m_type == TDoubleKeyframe::Expression || + (it - 1)->m_type == TDoubleKeyframe::File)) + return false; + + // inserting a new keyframe + k.m_value = value; + k.m_isKeyframe = true; + k.m_expression.setGrammar(m_imp->m_grammar); + k.m_expression.setOwnerParameter(this); + it = keyframes.insert(it, k); + if (it == keyframes.begin()) + it->m_prevType = TDoubleKeyframe::None; + else { + it->m_prevType = it[-1].m_type; + /*-- FxGuiでSegment内にKeyを打った場合は、Step値も引き継ぐ --*/ + it->m_step = it[-1].m_step; + } + if (it + 1 != keyframes.end()) + it[1].m_prevType = it->m_type; + + index = std::distance(keyframes.begin(), it); + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + created = true; + } + assert(0 == index || keyframes[index - 1].m_frame < keyframes[index].m_frame); + assert(getKeyframeCount() - 1 == index || keyframes[index + 1].m_frame > keyframes[index].m_frame); + + return created; +} + +//--------------------------------------------------------- + +void TDoubleParam::setKeyframe(int index, const TDoubleKeyframe &k) +{ + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + assert(0 <= index && index < (int)keyframes.size()); + + TActualDoubleKeyframe &dst = keyframes[index]; + TActualDoubleKeyframe oldKeyframe = dst; + + (TDoubleKeyframe &)dst = k; + dst.updateUnit(m_imp->m_measure); + + if (dst.m_type == TDoubleKeyframe::Expression || dst.m_type == TDoubleKeyframe::SimilarShape) + dst.m_expression.setText(dst.m_expressionText); + if (dst.m_type == TDoubleKeyframe::File) + dst.m_fileData.setParams(dst.m_fileParams); + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + + assert(0 == index || keyframes[index - 1].m_frame < keyframes[index].m_frame); + assert(getKeyframeCount() - 1 == index || keyframes[index + 1].m_frame > keyframes[index].m_frame); + if (index == 0) + dst.m_prevType = TDoubleKeyframe::None; + else + dst.m_prevType = keyframes[index - 1].m_type; +} + +//--------------------------------------------------------- + +void TDoubleParam::setKeyframes(const std::map &ks) +{ + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + + std::map::const_iterator it; + for (it = ks.begin(); it != ks.end(); ++it) { + int index = it->first; + assert(0 <= index && index < (int)keyframes.size()); + + TActualDoubleKeyframe oldKeyframe = keyframes[index]; + TActualDoubleKeyframe &dst = keyframes[index]; + + (TDoubleKeyframe &)dst = it->second; + dst.updateUnit(m_imp->m_measure); + + if (dst.m_type == TDoubleKeyframe::Expression || dst.m_type == TDoubleKeyframe::SimilarShape) + dst.m_expression.setText(dst.m_expressionText); + if (dst.m_type == TDoubleKeyframe::File) + dst.m_fileData.setParams(dst.m_fileParams); + } + if (!keyframes.empty()) { + keyframes[0].m_prevType = TDoubleKeyframe::None; + for (int i = 1; i < (int)keyframes.size(); i++) + keyframes[i].m_prevType = keyframes[i - 1].m_type; + } + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + +#ifndef NDEBUG + for (int i = 0; i + 1 < (int)keyframes.size(); i++) { + assert(keyframes[i].m_frame <= keyframes[i + 1].m_frame); + } +#endif +} + +//--------------------------------------------------------- + +void TDoubleParam::setKeyframe(const TDoubleKeyframe &k) +{ + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + DoubleKeyframeVector::iterator it; + it = std::lower_bound(keyframes.begin(), keyframes.end(), k); + if (it != keyframes.end() && it->m_frame == k.m_frame) { + // int index = std::distance(keyframes.begin(), it); + TActualDoubleKeyframe &dst = *it; + (TDoubleKeyframe &)dst = k; + dst.updateUnit(m_imp->m_measure); + } else { + it = keyframes.insert(it, TActualDoubleKeyframe(k)); + // int index = std::distance(keyframes.begin(), it); + //TDoubleKeyframe oldKeyframe = *it; + it->m_expression.setGrammar(m_imp->m_grammar); + it->m_expression.setOwnerParameter(this); + it->updateUnit(m_imp->m_measure); + } + it->m_isKeyframe = true; + + if (it->m_type == TDoubleKeyframe::Expression) + it->m_expression.setText(it->m_expressionText); + + if (it->m_type == TDoubleKeyframe::File) + it->m_fileData.setParams(it->m_fileParams); + + if (it == keyframes.begin()) + it->m_prevType = TDoubleKeyframe::None; + else + it->m_prevType = it[-1].m_type; + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + + assert(it == keyframes.begin() || (it - 1)->m_frame < it->m_frame); + assert(it + 1 == keyframes.end() || (it + 1)->m_frame > it->m_frame); +} + +//--------------------------------------------------------- + +void TDoubleParam::setDefaultValue(double value) +{ + assert(m_imp); + if (m_imp->m_defaultValue != value) { + m_imp->m_defaultValue = value; + + // gmt, 19-6-2010; needed to get a notification in the FxParamsGraphicEditor (Camera Stand) + // when a param (without keyframes) is changed + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); + } +} + +//--------------------------------------------------------- + +bool TDoubleParam::isDefault() const +{ + return m_imp->m_keyframes.empty() && + // m_imp->m_type == Keyframes && + m_imp->m_defaultValue == 0; +} + +//--------------------------------------------------------- + +int TDoubleParam::getKeyframeCount() const +{ + return m_imp->m_keyframes.size(); +} + +//--------------------------------------------------------- + +void TDoubleParam::getKeyframes(std::set &frames) const +{ + for (DoubleKeyframeVector::iterator it = m_imp->m_keyframes.begin(); + it != m_imp->m_keyframes.end(); ++it) + frames.insert(it->m_frame); +} + +//--------------------------------------------------------- + +bool TDoubleParam::hasKeyframes() const +{ + return !m_imp->m_keyframes.empty(); +} + +//--------------------------------------------------------- + +const TDoubleKeyframe &TDoubleParam::getKeyframe(int index) const +{ + assert(0 <= index && index < (int)m_imp->m_keyframes.size()); + return m_imp->m_keyframes[index]; +} + +//--------------------------------------------------------- + +const TDoubleKeyframe &TDoubleParam::getKeyframeAt(double frame) const +{ + static TDoubleKeyframe k; + k = TDoubleKeyframe(); + int i = 0; + for (i = 0; i < (int)m_imp->m_keyframes.size(); i++) + if (m_imp->m_keyframes[i].m_frame >= frame) + break; + if (i < (int)m_imp->m_keyframes.size() && m_imp->m_keyframes[i].m_frame == frame) { + k = m_imp->m_keyframes[i]; + return k; + } + k.m_frame = frame; + k.m_value = getValue(frame); + k.m_isKeyframe = false; + return k; +} + +//--------------------------------------------------------- + +bool TDoubleParam::isKeyframe(double frame) const +{ + return std::binary_search( + m_imp->m_keyframes.begin(), + m_imp->m_keyframes.end(), + TDoubleKeyframe(frame)); +} + +//--------------------------------------------------------- + +void TDoubleParam::deleteKeyframe(double frame) +{ + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + DoubleKeyframeVector::iterator it; + it = std::lower_bound(keyframes.begin(), keyframes.end(), + TDoubleKeyframe(frame)); + if (it == keyframes.end() || it->m_frame != frame) + return; + + TDoubleKeyframe::Type type = it->m_prevType; + it = m_imp->m_keyframes.erase(it); + if (it != keyframes.end()) + it->m_prevType = type; + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); +} + +//--------------------------------------------------------- + +void TDoubleParam::clearKeyframes() +{ + m_imp->m_keyframes.clear(); + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); +} + +//--------------------------------------------------------- + +void TDoubleParam::assignKeyframe( + double frame, + const TParamP &src, double srcFrame, + bool changedOnly) +{ + TDoubleParamP dp = src; + if (!dp) + return; + double value = dp->getValue(srcFrame); + if (!changedOnly || getValue(frame) != value) + setValue(frame, value); +} + +//--------------------------------------------------------- + +int TDoubleParam::getClosestKeyframe(double frame) const +{ + DoubleKeyframeVector &keyframes = m_imp->m_keyframes; + typedef DoubleKeyframeVector::iterator Iterator; + Iterator it = std::lower_bound(keyframes.begin(), keyframes.end(), + TDoubleKeyframe(frame)); + if (it == keyframes.end()) { + // frame > k.m_frame per qualsiasi k. + // ritorna l'indice dell'ultimo keyframe (o -1 se l'array e' vuoto) + return keyframes.size() - 1; + } else { + int index = std::distance(keyframes.begin(), it); + if (it->m_frame == frame || index == 0) + return index; + else { + double nextFrame = it->m_frame; + double prevFrame = keyframes[index - 1].m_frame; + assert(prevFrame < frame && frame < nextFrame); + return (nextFrame - frame < frame - prevFrame) ? index : index - 1; + } + } +} + +//--------------------------------------------------------- + +int TDoubleParam::getNextKeyframe(double frame) const +{ + TDoubleKeyframe k(frame); + typedef DoubleKeyframeVector::const_iterator Iterator; + Iterator it = std::upper_bound(m_imp->m_keyframes.begin(), m_imp->m_keyframes.end(), k); + if (it == m_imp->m_keyframes.end()) + return -1; + int index = std::distance(m_imp->m_keyframes.begin(), it); + if (it->m_frame == frame) { + ++index; + return index < getKeyframeCount() ? index : -1; + } + return index; +} + +//--------------------------------------------------------- + +int TDoubleParam::getPrevKeyframe(double frame) const +{ + TDoubleKeyframe k(frame); + typedef DoubleKeyframeVector::const_iterator Iterator; + Iterator it = std::lower_bound(m_imp->m_keyframes.begin(), m_imp->m_keyframes.end(), k); + if (it == m_imp->m_keyframes.end()) + return m_imp->m_keyframes.size() - 1; + int index = std::distance(m_imp->m_keyframes.begin(), it); + return index - 1; +} + +//--------------------------------------------------------- + +double TDoubleParam::keyframeIndexToFrame(int index) const +{ + assert(0 <= index && index < getKeyframeCount()); + return getKeyframe(index).m_frame; +} + +//--------------------------------------------------------- + +void TDoubleParam::addObserver(TParamObserver *observer) +{ + m_imp->m_observers.insert(observer); +} + +//--------------------------------------------------------- + +void TDoubleParam::removeObserver(TParamObserver *observer) +{ + m_imp->m_observers.erase(observer); +} + +//--------------------------------------------------------- + +const std::set &TDoubleParam::observers() const +{ + return m_imp->m_observers; +} + +//--------------------------------------------------------- + +void TDoubleParam::loadData(TIStream &is) +{ + string tagName; + /* + if(is.matchTag(tagName)) + { + // nuovo formato (29 agosto 2005) + if(tagName!="type") throw TException("expected "); + int type = 0; + is >> type; + // m_imp->m_type = TDoubleParam::Type(type); + if(!is.matchEndTag()) throw TException(tagName + " : missing endtag"); + + + //if(!is.matchTag(tagName) || tagName != "default") throw TException("expected "); + //is >> m_imp->m_defaultValue; + //if(!is.matchEndTag()) throw TException(tagName + " : missing endtag"); + //if(!is.matchTag(tagName) || tagName != "default") throw TException("expected "); + + + } + else + { + // vecchio formato + is >> m_imp->m_defaultValue; + } + */ + + m_imp->m_keyframes.clear(); + int oldType = -1; + while (is.matchTag(tagName)) { + if (tagName == "type") { + // (old) format 5.2. Since 6.0 param type is no used anymore + is >> oldType; + } else if (tagName == "default") { + is >> m_imp->m_defaultValue; + } else if (tagName == "cycle") { + string dummy; + is >> dummy; + m_imp->m_cycleEnabled = true; + //setExtrapolationAfter(Loop); + } else if (tagName == "k") { + // vecchio formato + if (oldType != 0) + continue; + int linkStatus = 0; + TActualDoubleKeyframe k; + is >> k.m_frame >> k.m_value >> k.m_speedIn.x >> k.m_speedIn.y >> k.m_speedOut.x >> k.m_speedOut.y >> linkStatus; + + k.m_isKeyframe = true; + k.m_linkedHandles = (linkStatus & 1) != 0; + + if ((linkStatus & 1) != 0) + k.m_type = TDoubleKeyframe::SpeedInOut; + else if ((linkStatus & 2) != 0 || (linkStatus & 4) != 0) + k.m_type = TDoubleKeyframe::EaseInOut; + else + k.m_type = TDoubleKeyframe::Linear; + k.m_expression.setGrammar(m_imp->m_grammar); + k.m_expression.setOwnerParameter(this); + k.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + m_imp->m_keyframes.push_back(k); + } else if (tagName == "expr") { + // vecchio formato + if (oldType != 1) + continue; + string text = is.getTagAttribute("text"); + string enabled = is.getTagAttribute("enabled"); + TDoubleKeyframe kk1, kk2; + kk1.m_frame = -1000; + kk2.m_frame = 1000; + kk1.m_isKeyframe = true; + kk2.m_isKeyframe = true; + kk1.m_expressionText = text; + kk2.m_expressionText = text; + kk1.m_type = TDoubleKeyframe::Expression; + kk2.m_type = TDoubleKeyframe::Expression; + TActualDoubleKeyframe k1(kk1); + TActualDoubleKeyframe k2(kk2); + k1.m_expression.setGrammar(m_imp->m_grammar); + k1.m_expression.setOwnerParameter(this); + k1.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + k2.m_expression.setGrammar(m_imp->m_grammar); + k2.m_expression.setOwnerParameter(this); + k2.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + m_imp->m_keyframes.push_back(k1); + m_imp->m_keyframes.push_back(k2); + continue; + } else if (tagName == "file") { + // vecchio formato + if (oldType != 2) + continue; + TDoubleKeyframe::FileParams params; + params.m_path = TFilePath(is.getTagAttribute("path")); + params.m_fieldIndex = toInt(is.getTagAttribute("index")); + TActualDoubleKeyframe k1, k2; + k1.m_frame = -1000; + k2.m_frame = 1000; + k1.m_isKeyframe = true; + k2.m_isKeyframe = true; + k1.m_fileData.setParams(params); + k2.m_fileData.setParams(params); + k1.m_type = TDoubleKeyframe::File; + k2.m_type = TDoubleKeyframe::File; + k1.m_expression.setGrammar(m_imp->m_grammar); + k1.m_expression.setOwnerParameter(this); + k1.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + k2.m_expression.setGrammar(m_imp->m_grammar); + k2.m_expression.setOwnerParameter(this); + k2.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + m_imp->m_keyframes.push_back(k1); + m_imp->m_keyframes.push_back(k2); + continue; + } else if (tagName == "step") { + int step = 0; + is >> step; + //if(step>1) + // m_imp->m_frameStep = step; + } else if (tagName == "keyframes") { + while (!is.eos()) { + TDoubleKeyframe kk; + kk.loadData(is); + TActualDoubleKeyframe k(kk); + k.m_expression.setGrammar(m_imp->m_grammar); + k.m_expression.setOwnerParameter(this); + k.m_prevType = m_imp->m_keyframes.empty() ? TDoubleKeyframe::None : m_imp->m_keyframes.back().m_type; + m_imp->m_keyframes.push_back(k); + } + } else { + throw TException(tagName + " : unexpected tag"); + } + if (!is.matchEndTag()) + throw TException(tagName + " : missing endtag"); + } + if (m_imp->m_keyframes.empty() && !is.eos()) { + // vecchio sistema (prima 16/1/2003) + while (!is.eos()) { + double t, v; + is >> t >> v; + m_imp->m_keyframes.push_back(TActualDoubleKeyframe(t, v)); + } + if (!m_imp->m_keyframes.empty()) { + m_imp->m_keyframes[0].m_prevType = TDoubleKeyframe::None; + for (int i = 1; i < (int)m_imp->m_keyframes.size(); i++) + m_imp->m_keyframes[i].m_prevType = m_imp->m_keyframes[i - 1].m_type; + } + } + + m_imp->notify(TParamChange(this, 0, 0, true, false, false)); +} + +//--------------------------------------------------------- + +void TDoubleParam::saveData(TOStream &os) +{ + // os.child("type") << (int)m_imp->m_type; + os.child("default") << m_imp->m_defaultValue; + if (isCycleEnabled()) + os.child("cycle") << std::string("enabled"); + //if(getExtrapolationAfter() == Loop) + // os.child("cycle") << string("loop"); + if (!m_imp->m_keyframes.empty()) { + os.openChild("keyframes"); + DoubleKeyframeVector::const_iterator it; + for (it = m_imp->m_keyframes.begin(); it != m_imp->m_keyframes.end(); ++it) + it->saveData(os); + os.closeChild(); + } +} + +//--------------------------------------------------------- + +string TDoubleParam::getStreamTag() const +{ + return "doubleParam"; +} + +//------------------------------------------------------------------- + +string TDoubleParam::getValueAlias(double frame, int precision) +{ + return toString(getValue(frame), precision); +} + +//------------------------------------------------------------------- + +void TDoubleParam::setGrammar(const TSyntax::Grammar *grammar) +{ + m_imp->m_grammar = grammar; + for (int i = 0; i < (int)m_imp->m_keyframes.size(); i++) + m_imp->m_keyframes[i].m_expression.setGrammar(grammar); +} + +//------------------------------------------------------------------- + +const TSyntax::Grammar *TDoubleParam::getGrammar() const +{ + return m_imp->m_grammar; +} + +//------------------------------------------------------------------- + +void TDoubleParam::accept(TSyntax::CalculatorNodeVisitor &visitor) +{ + for (int i = 0; i < (int)m_imp->m_keyframes.size(); i++) + if (m_imp->m_keyframes[i].m_type == TDoubleKeyframe::Expression || m_imp->m_keyframes[i].m_type == TDoubleKeyframe::SimilarShape) + m_imp->m_keyframes[i].m_expression.accept(visitor); +} + +//------------------------------------------------------------------- + +string TDoubleParam::getMeasureName() const +{ + return m_imp->m_measureName; +} + +//------------------------------------------------------------------- + +void TDoubleParam::setMeasureName(string name) +{ + m_imp->m_measureName = name; + m_imp->m_measure = TMeasureManager::instance()->get(name); +} + +//------------------------------------------------------------------- + +TMeasure *TDoubleParam::getMeasure() const +{ + return m_imp->m_measure; +} diff --git a/toonz/sources/common/tparam/tdoubleparamfile.cpp b/toonz/sources/common/tparam/tdoubleparamfile.cpp new file mode 100644 index 0000000..170a84d --- /dev/null +++ b/toonz/sources/common/tparam/tdoubleparamfile.cpp @@ -0,0 +1,143 @@ + + +#include "tdoubleparamfile.h" +#include "tfilepath_io.h" +#include "tconvert.h" + +namespace +{ + +//--------------------------------------------------------- + +bool parseDouble(double &value, char *&s) +{ + if (!(*s == '-' || *s == '.' || '0' <= *s && *s <= '9')) + return false; + char *t = s; + if (*s == '-') + s++; + while ('0' <= *s && *s <= '9') + s++; + if (*s == '.') { + s++; + while ('0' <= *s && *s <= '9') + s++; + if (*s == 'e' || *s == 'E') { + s++; + if (*s == '+' || *s == '-') + s++; + while ('0' <= *s && *s <= '9') + s++; + } + } + string w(t, s - t); + value = toDouble(w); + return true; +} + +//--------------------------------------------------------- + +int split(std::vector &values, char *s) +{ + int count = 0; + for (;;) { + while (*s == ' ' || *s == '\t') + s++; + if (*s == '\0') + break; + double value = 0; + bool found = parseDouble(value, s); + if (found) { + count++; + values.push_back(value); + while (*s == ' ' || *s == '\t') + s++; + } + if (*s == ',' || *s == ';') { + if (!found) { + count++; + values.push_back(0); + } + s++; + } else { + if (!found) + break; + } + } + return count; +} + +//--------------------------------------------------------- +} // namespace +//--------------------------------------------------------- + +TDoubleParamFileData::TDoubleParamFileData() + : m_params(), m_dirtyFlag(false) +{ +} + +//--------------------------------------------------------- + +TDoubleParamFileData::~TDoubleParamFileData() +{ +} + +//--------------------------------------------------------- + +void TDoubleParamFileData::setParams(const TDoubleKeyframe::FileParams ¶ms) +{ + m_params = params; + invalidate(); +} + +//--------------------------------------------------------- + +void TDoubleParamFileData::invalidate() +{ + m_dirtyFlag = true; + m_values.clear(); +} + +//--------------------------------------------------------- + +double TDoubleParamFileData::getValue(double frame, double defaultValue) +{ + if (frame < 0) + return defaultValue; + int intFrame = (int)frame; + if (m_dirtyFlag) + read(); + if (intFrame >= (int)m_values.size()) + return defaultValue; + else + return m_values[intFrame]; +} + +//--------------------------------------------------------- + +void TDoubleParamFileData::read() +{ + m_dirtyFlag = false; + m_values.clear(); + int k = m_params.m_fieldIndex; + if (k < 0) + return; + TFilePath fp = m_params.m_path; + Tifstream is(fp); + char buffer[2048]; + memset(buffer, 0, sizeof buffer); + while (is) { + is.getline(buffer, sizeof(buffer) - 1); + std::vector fields; + QString line(buffer); + if (line.size() == 0 || line.startsWith("#")) + continue; + split(fields, buffer); + double value = 0; + if (k < (int)fields.size()) + value = fields[k]; + m_values.push_back(value); + } +} + +//--------------------------------------------------------- diff --git a/toonz/sources/common/tparam/tdoubleparamfile.h b/toonz/sources/common/tparam/tdoubleparamfile.h new file mode 100644 index 0000000..f542550 --- /dev/null +++ b/toonz/sources/common/tparam/tdoubleparamfile.h @@ -0,0 +1,38 @@ + + +#ifndef TDOUBLEPARAMFILE_INCLUDED +#define TDOUBLEPARAMFILE_INCLUDED + +#include "tdoublekeyframe.h" + +#undef DVAPI +#undef DVVAR +#ifdef TPARAM_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class DVAPI TDoubleParamFileData +{ + TDoubleKeyframe::FileParams m_params; + std::vector m_values; + bool m_dirtyFlag; + + void read(); + +public: + TDoubleParamFileData(); + ~TDoubleParamFileData(); + + void setParams(const TDoubleKeyframe::FileParams ¶ms); + const TDoubleKeyframe::FileParams &getParams() { return m_params; } + + void invalidate(); + + double getValue(double frame, double defaultValue = 0); +}; + +#endif diff --git a/toonz/sources/common/tparam/tdoubleparamrelayproperty.cpp b/toonz/sources/common/tparam/tdoubleparamrelayproperty.cpp new file mode 100644 index 0000000..1e34197 --- /dev/null +++ b/toonz/sources/common/tparam/tdoubleparamrelayproperty.cpp @@ -0,0 +1,109 @@ + + +#include "tdoubleparamrelayproperty.h" + +//***************************************************************************** +// TDoubleParamRelayProperty implementation +//***************************************************************************** + +TDoubleParamRelayProperty::TDoubleParamRelayProperty(const std::string &name, TDoubleParamP param) + : TProperty(name), m_frame() +{ + if (param) + setParam(param); +} + +//------------------------------------------------------------------- + +TDoubleParamRelayProperty::~TDoubleParamRelayProperty() +{ + if (m_param) + m_param->removeObserver(this); +} + +//------------------------------------------------------------------- + +TDoubleParamRelayProperty::TDoubleParamRelayProperty(const TDoubleParamRelayProperty &other) + : TProperty(other), m_param(other.m_param), m_frame(other.m_frame) +{ + if (m_param) + m_param->addObserver(this); +} + +//------------------------------------------------------------------- + +TDoubleParamRelayProperty &TDoubleParamRelayProperty::operator=(const TDoubleParamRelayProperty &other) +{ + TProperty::operator=(other); + + if (m_param) + m_param->removeObserver(this); + + m_param = other.m_param; + m_frame = other.m_frame; + + if (m_param) + m_param->addObserver(this); + + return *this; +} + +//------------------------------------------------------------------- + +TProperty *TDoubleParamRelayProperty::clone() const +{ + return new TDoubleParamRelayProperty(*this); +} + +//------------------------------------------------------------------- + +std::string TDoubleParamRelayProperty::getValueAsString() +{ + return m_param ? toString(m_param->getValue(m_frame)) : ""; +} + +//------------------------------------------------------------------- + +void TDoubleParamRelayProperty::setParam(const TDoubleParamP ¶m) +{ + if (m_param == param) + return; + + if (m_param) + m_param->removeObserver(this); + + m_param = param; + + if (param) + param->addObserver(this); +} + +//------------------------------------------------------------------- + +void TDoubleParamRelayProperty::setValue(double v) +{ + if (m_param) + m_param->setValue(m_frame, v); +} + +//------------------------------------------------------------------- + +double TDoubleParamRelayProperty::getValue() const +{ + return m_param ? m_param->getValue(m_frame) : 0.0; +} + +//------------------------------------------------------------------- + +void TDoubleParamRelayProperty::accept(TProperty::Visitor &v) +{ + if (TDoubleParamRelayProperty::Visitor *vv = dynamic_cast(&v)) + vv->visit(this); +} + +//------------------------------------------------------------------- + +void TDoubleParamRelayProperty::onChange(const TParamChange &) +{ + notifyListeners(); +} diff --git a/toonz/sources/common/tparam/tnotanimatableparam.cpp b/toonz/sources/common/tparam/tnotanimatableparam.cpp new file mode 100644 index 0000000..414a302 --- /dev/null +++ b/toonz/sources/common/tparam/tnotanimatableparam.cpp @@ -0,0 +1,291 @@ + + +#include "tnotanimatableparam.h" +#include "tstream.h" + +PERSIST_IDENTIFIER(TIntParam, "intParam") +PERSIST_IDENTIFIER(TBoolParam, "boolParam") +PERSIST_IDENTIFIER(TFilePathParam, "filePathParam") +PERSIST_IDENTIFIER(TStringParam, "stringParam") +PERSIST_IDENTIFIER(TNADoubleParam, "naDoubleParam") +//PERSIST_IDENTIFIER(TIntEnumParam, "intEnumParam") + +TPersistDeclarationT TEnumParam::m_declaration("intEnumParam"); + +//========================================================= + +void TIntParam::loadData(TIStream &is) +{ + int def, value; + is >> def; + if (is.eos()) { + def += 1; + setDefaultValue(def); + setValue(def, false); + return; + } + setDefaultValue(def); + is >> value; + setValue(value, false); +} + +//--------------------------------------------------------- + +void TIntParam::saveData(TOStream &os) +{ + os << getDefaultValue(); + os << getValue(); +} +//--------------------------------------------------------- + +bool TIntParam::isWheelEnabled() const +{ + return m_isWheelEnabled; +} +//--------------------------------------------------------- + +void TIntParam::enableWheel(bool on) +{ + m_isWheelEnabled = on; +} + +//========================================================= + +void TBoolParam::loadData(TIStream &is) +{ + int def, value; + is >> def >> value; + setDefaultValue(def ? true : false); + setValue(value ? true : false, false); +} + +//--------------------------------------------------------- + +void TBoolParam::saveData(TOStream &os) +{ + os << (int)getDefaultValue() << (int)getValue(); +} + +//========================================================= + +void TFilePathParam::loadData(TIStream &is) +{ + TFilePath def, value; + is >> def >> value; + setDefaultValue(def); + setValue(value, false); +} + +//--------------------------------------------------------- + +void TFilePathParam::saveData(TOStream &os) +{ + os << getDefaultValue(); + os << getValue(); +} + +//========================================================= +void TStringParam::loadData(TIStream &is) +{ + wstring def, value; + is >> def >> value; + setDefaultValue(def); + setValue(value, false); +} +void TStringParam::saveData(TOStream &os) +{ + os << getDefaultValue(); + os << getValue(); +} +//========================================================= +void TNADoubleParam::loadData(TIStream &is) +{ + double def, value; + is >> def >> value; + setDefaultValue(def); + setValue(value, false); +} +void TNADoubleParam::saveData(TOStream &os) +{ + os << getDefaultValue(); + os << getValue(); +} +//========================================================= + +//========================================================= + +namespace +{ +template +class matchesValue +{ + T m_v; + +public: + matchesValue(T v) : m_v(v) {} + bool operator()(const pair &p) { return m_v == p.first; } +}; +} + +//--------------------------------------------------------- + +class TEnumParamImp +{ +public: + vector> m_items; + void copy(TEnumParamImp *src) + { + m_items.clear(); + std::back_insert_iterator>> bii(m_items); + std::copy(src->m_items.begin(), src->m_items.end(), bii); + } + bool checkValue(int v) + { + std::vector>::iterator it = + std::find_if(m_items.begin(), m_items.end(), matchesValue(v)); + return it != m_items.end(); + } +}; + +//--------------------------------------------------------- + +TEnumParam::TEnumParam(const int &v, const string &caption) + : TNotAnimatableParam(v), m_imp(new TEnumParamImp()) +{ + addItem(v, caption); +} + +//--------------------------------------------------------- + +void TEnumParam::copy(TParam *src) +{ + TNotAnimatableParam::copy(src); + TEnumParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + TEnumParam::setName(src->getName()); + m_imp->copy(p->m_imp); +} + +//--------------------------------------------------------- + +TEnumParam::~TEnumParam() +{ + delete m_imp; +} + +//--------------------------------------------------------- + +TEnumParam::TEnumParam(const TEnumParam &src) + : TNotAnimatableParam(src), m_imp(new TEnumParamImp(*src.m_imp)) +{ +} + +//--------------------------------------------------------- + +TEnumParam::TEnumParam() + : TNotAnimatableParam(), m_imp(new TEnumParamImp()) + +{ +} + +//--------------------------------------------------------- + +void TEnumParam::setValue(int v, bool undoing) +{ + bool valid = false; + std::vector>::iterator it = m_imp->m_items.begin(); + for (; it != m_imp->m_items.end(); ++it) { + if (it->first == v) { + valid = true; + break; + } + } + + if (!valid) + throw TException("out of range parameter value"); + + TNotAnimatableParam::setValue(v, undoing); +} + +//--------------------------------------------------------- + +void TEnumParam::setValue(const string &caption, bool undoing) +{ + bool valid = false; + int v = 0; + std::vector>::iterator it = m_imp->m_items.begin(); + for (; it != m_imp->m_items.end(); ++it) { + if (it->second == caption) { + v = it->first; + valid = true; + break; + } + } + + if (!valid) + throw TException("out of range parameter value"); + + TNotAnimatableParam::setValue(v, undoing); +} + +//--------------------------------------------------------- + +void TEnumParam::addItem(const int &item, const string &caption) +{ + m_imp->m_items.push_back(std::make_pair(item, caption)); +} + +//--------------------------------------------------------- + +int TEnumParam::getItemCount() const +{ + return m_imp->m_items.size(); +} + +//--------------------------------------------------------- + +void TEnumParam::getItem(int i, int &item, string &caption) const +{ + assert(i >= 0 && i < m_imp->m_items.size()); + item = m_imp->m_items[i].first; + caption = m_imp->m_items[i].second; +} + +//--------------------------------------------------------- + +void TEnumParam::loadData(TIStream &is) +{ + int value; + is >> value; + + try { + setValue(value, false); + } catch (TException &) { + TNotAnimatableParam::reset(); + } +} + +//--------------------------------------------------------- + +void TEnumParam::saveData(TOStream &os) +{ + os << TNotAnimatableParam::getValue(); +} + +//--------------------------------------------------------- + +void TIntParam::setValueRange(int min, int max) +{ + assert(min < max); + + minValue = min; + maxValue = max; +} + +bool TIntParam::getValueRange(int &min, int &max) const +{ + min = minValue; + max = maxValue; + return min < max; +} diff --git a/toonz/sources/common/tparam/tparam.cpp b/toonz/sources/common/tparam/tparam.cpp new file mode 100644 index 0000000..f2bb84c --- /dev/null +++ b/toonz/sources/common/tparam/tparam.cpp @@ -0,0 +1,19 @@ + + +// TnzCore includes +#include "tparam.h" +#include "tparamchange.h" + +DEFINE_CLASS_CODE(TParam, 11) + +double TParamChange::m_minFrame = -(std::numeric_limits::max)(); +double TParamChange::m_maxFrame = +(std::numeric_limits::max)(); + +//------------------------------------------------------------------------------ + +TParamChange::TParamChange(TParam *param, + double firstAffectedFrame, double lastAffectedFrame, + bool keyframeChanged, bool dragging, bool undoing) + : m_param(param), m_firstAffectedFrame(firstAffectedFrame), m_lastAffectedFrame(lastAffectedFrame), m_keyframeChanged(keyframeChanged), m_dragging(dragging), m_undoing(undoing) +{ +} diff --git a/toonz/sources/common/tparam/tparamcontainer.cpp b/toonz/sources/common/tparam/tparamcontainer.cpp new file mode 100644 index 0000000..f489dbc --- /dev/null +++ b/toonz/sources/common/tparam/tparamcontainer.cpp @@ -0,0 +1,135 @@ + + +#include "tparamcontainer.h" +//#include "tdoubleparam.h" +#include "tparamset.h" + +#include "tparam.h" + +void TParamVar::setParamObserver(TParamObserver *obs) +{ + if (m_paramObserver == obs) + return; + TParam *param = getParam(); + if (param) { + if (obs) + param->addObserver(obs); + if (m_paramObserver) + param->removeObserver(m_paramObserver); + } + m_paramObserver = obs; +} + +class TParamContainer::Imp +{ +public: + std::map m_nameTable; + std::vector m_vars; + TParamObserver *m_paramObserver; + Imp() : m_paramObserver(0) {} + ~Imp() { clearPointerContainer(m_vars); } +}; + +TParamContainer::TParamContainer() + : m_imp(new Imp()) +{ +} + +TParamContainer::~TParamContainer() +{ + delete m_imp; +} + +void TParamContainer::setParamObserver(TParamObserver *observer) +{ + m_imp->m_paramObserver = observer; +} + +TParamObserver *TParamContainer::getParamObserver() const +{ + return m_imp->m_paramObserver; +} + +void TParamContainer::add(TParamVar *var) +{ + m_imp->m_vars.push_back(var); + m_imp->m_nameTable[var->getName()] = var; + var->setParamObserver(m_imp->m_paramObserver); + var->getParam()->setName(var->getName()); +} + +int TParamContainer::getParamCount() const +{ + return m_imp->m_vars.size(); +} + +TParam *TParamContainer::getParam(int index) const +{ + assert(0 <= index && index < getParamCount()); + return m_imp->m_vars[index]->getParam(); +} + +bool TParamContainer::isParamHidden(int index) const +{ + assert(0 <= index && index < getParamCount()); + return m_imp->m_vars[index]->isHidden(); +} + +string TParamContainer::getParamName(int index) const +{ + assert(0 <= index && index < getParamCount()); + return m_imp->m_vars[index]->getName(); +} + +const TParamVar *TParamContainer::getParamVar(int index) const +{ + assert(0 <= index && index < getParamCount()); + return m_imp->m_vars[index]; +} + +TParam *TParamContainer::getParam(string name) const +{ + std::map::const_iterator it; + it = m_imp->m_nameTable.find(name); + if (it == m_imp->m_nameTable.end()) + return 0; + else + return it->second->getParam(); +} + +void TParamContainer::unlink() +{ + for (int i = 0; i < getParamCount(); i++) { + //TRangeParam *p0;//,*p1; + TParamVar *var = m_imp->m_vars[i]; + TParam *param = var->getParam(); + //p0 = dynamic_cast(param); + var->setParam(param->clone()); + /*p1 = dynamic_cast(var->getParam()); + if(p0 && p1) + { + string name = p0->getName(); + name = p1->getName(); + }*/ + } +} + +void TParamContainer::link(const TParamContainer *src) +{ + assert(getParamCount() == src->getParamCount()); + for (int i = 0; i < getParamCount(); i++) { + assert(getParamName(i) == src->getParamName(i)); + assert(m_imp->m_vars[i]->getName() == getParamName(i)); + m_imp->m_vars[i]->setParam(src->getParam(i)); + } +} + +void TParamContainer::copy(const TParamContainer *src) +{ + assert(getParamCount() == src->getParamCount()); + for (int i = 0; i < getParamCount(); i++) { + assert(getParamName(i) == src->getParamName(i)); + assert(m_imp->m_vars[i]->getName() == getParamName(i)); + getParam(i)->copy(src->getParam(i)); + } +} diff --git a/toonz/sources/common/tparam/tparamset.cpp b/toonz/sources/common/tparam/tparamset.cpp new file mode 100644 index 0000000..49376de --- /dev/null +++ b/toonz/sources/common/tparam/tparamset.cpp @@ -0,0 +1,623 @@ + + +#include "tparamset.h" +#include "tundo.h" +//#include "tparam.h" +#include "tdoubleparam.h" +#include "tstream.h" + +#include + +//--------------------------------------------------------- + +/* +class ChangeBlock { +public: + ChangeBlock() + : m_firstAffectedFrame ( TParamChange::m_maxFrame) + , m_lastAffectedFrame ( TParamChange::m_minFrame) + { + } + + ~ChangeBlock() + { + } + + void add(const TParamChange &change) + { + m_firstAffectedFrame = tmin(m_firstAffectedFrame, change.m_firstAffectedFrame); + m_lastAffectedFrame = tmax(m_lastAffectedFrame , change.m_lastAffectedFrame); + + m_changes.push_back(change.clone()); + } + vector m_changes; +double m_firstAffectedFrame; +double m_lastAffectedFrame; +}; +*/ +namespace +{ +void doRelease(const pair ¶m) +{ + param.first->release(); +} +}; + +//------------------------------------------------------------------------------ + +class TParamSetImp : public TParamObserver +{ + friend class TParamSet; + TParamSet *m_param; + vector> m_params; + + // ChangeBlock *m_changeBlock; + bool m_draggingEnabled, m_notificationEnabled; + +public: + TParamSetImp(TParamSet *param) : m_param(param) + //, m_changeBlock(0) + , + m_draggingEnabled(false), m_notificationEnabled(true) + { + } + + ~TParamSetImp() + { + std::for_each(m_params.begin(), m_params.end(), doRelease); + } + // std::set m_observers; + std::set m_paramObservers; + + template + void notify(const T &change); + + void onChange(const TParamChange &change) + { + + /* + if (!m_changeBlock) // se non stiamo modificando un blocco di parametri, invio la notifica + { + vector params; + params.push_back(change.m_param); + + TParamSetChange psChange(m_param, change.m_firstAffectedFrame, change.m_lastAffectedFrame, + params, change.m_undoing); + notify(psChange); + } + else + notify(change); + + if (!m_changeBlock) // se non stiamo modificando un blocco di parametri, invio la notifica + notify(change); + else + { + //metto da parte la TParamChange, per poi alla fine notificare una TParamSetChange + m_changeBlock->add(change); + } + */ + } +}; + +//--------------------------------------------------------- + +TParamSet::TParamSet(string name) + : TParam(name) +{ + m_imp = new TParamSetImp(this); +} + +//--------------------------------------------------------- + +TParamSet::TParamSet(const TParamSet &src) + : TParam(src.getName()) +{ + m_imp = new TParamSetImp(this); +} + +//--------------------------------------------------------- + +TParamSet::~TParamSet() +{ + delete m_imp; +} + +//--------------------------------------------------------- +/* +template +class MyBackInsertIterator : public std::iterator +{ +protected: + Container &container; +public: + MyBackInsertIterator(Container &c) : container(c) {} + MyBackInsertIterator &operator=(const typename Container::value_type &value) + { + container.push_back(value); + return *this; + } + MyBackInsertIterator&operator*() + { + return *this; + } + MyBackInsertIterator&operator++() + { + return *this; + } + MyBackInsertIterator&operator++(int) + { + return *this; + } +}; +*/ + +//--------------------------------------------------------- + +void TParamSet::beginParameterChange() +{ + // assert(0); + + //std::set::iterator it = m_imp->m_observers.begin(); + //for (;it != m_imp->m_observers.end(); ++it) + // (*it)->onBeginChangeBlock(this); + + //assert(!m_imp->m_changeBlock); + + vector params; + + /* +MyBackInsertIterator > > myBackInsertIterator(params); +copy(m_imp->m_params.begin(), m_imp->m_params.end(), myIterator); +*/ + + std::vector>::iterator it2 = m_imp->m_params.begin(); + for (; it2 != m_imp->m_params.end(); ++it2) + params.push_back(it2->first); + + // m_imp->m_changeBlock = new ChangeBlock; +}; +//--------------------------------------------------------- + +void TParamSet::endParameterChange(){ + // assert(0); + //assert(m_imp->m_changeBlock); + /* +TParamSetChange change(this, m_imp->m_changeBlock->m_firstAffectedFrame, + m_imp->m_changeBlock->m_lastAffectedFrame, + m_imp->m_changeBlock->m_changes, false); + +change.m_dragging = m_imp->m_draggingEnabled; + +//delete m_imp->m_changeBlock; +//m_imp->m_changeBlock = 0; +//m_imp->notify(change); + +//std::set::iterator it = m_imp->m_observers.begin(); +//for (;it != m_imp->m_observers.end(); ++it) +// (*it)->onEndChangeBlock(this); + +m_imp->notify(change); +*/ +}; + +//--------------------------------------------------------- + +void TParamSet::addParam(const TParamP ¶m, const string &name) +{ + pair paramToInsert = std::make_pair(param.getPointer(), name); + std::vector>::iterator it = + std::find(m_imp->m_params.begin(), m_imp->m_params.end(), paramToInsert); + + if (it == m_imp->m_params.end()) { + param->addRef(); + param->addObserver(m_imp); + m_imp->m_params.push_back(paramToInsert); + //TParamSetParamAdded psParamAdded(this, param.getPointer(), name, false); + if (param->getName().empty()) + param->setName(name); + //m_imp->notify(psParamAdded); + } +} + +//--------------------------------------------------------- + +void TParamSet::insertParam(const TParamP ¶m, const string &name, int index) +{ + pair paramToInsert = std::make_pair(param.getPointer(), name); + std::vector>::iterator it = + std::find(m_imp->m_params.begin(), m_imp->m_params.end(), paramToInsert); + + if (it == m_imp->m_params.end()) { + param->addRef(); + param->addObserver(m_imp); + it = m_imp->m_params.begin(); + int f; + for (f = 0; f < index; f++) + it++; + m_imp->m_params.insert(it, paramToInsert); + if (param->getName().empty()) + param->setName(name); + } +} + +//--------------------------------------------------------- + +namespace +{ +class matchesParam +{ + TParamP m_param; + +public: + matchesParam(const TParamP ¶m) : m_param(param) {} + bool operator()(const pair ¶m) + { + return m_param.getPointer() == param.first; + } +}; +} + +//--------------------------------------------------------- + +void TParamSet::removeParam(const TParamP ¶m) +{ + std::vector>::iterator it = + std::find_if(m_imp->m_params.begin(), m_imp->m_params.end(), matchesParam(param)); + if (it != m_imp->m_params.end()) { + param->removeObserver(m_imp); + + //TParamSetParamRemoved psParamRemoved(this, param.getPointer(), it->second, false); + //m_imp->notify(psParamRemoved); + + param->release(); + m_imp->m_params.erase(it); + } +} + +//--------------------------------------------------------- + +void TParamSet::removeAllParam() +{ + while (!m_imp->m_params.empty()) { + std::vector>::iterator it = m_imp->m_params.begin(); + TParam *param = it->first; + param->removeObserver(m_imp); + param->release(); + m_imp->m_params.erase(it); + } +} + +//--------------------------------------------------------- + +int TParamSet::getParamCount() const +{ + return m_imp->m_params.size(); +} + +//--------------------------------------------------------- + +TParamP TParamSet::getParam(int i) const +{ + assert(i >= 0 && i < (int)m_imp->m_params.size()); + return m_imp->m_params[i].first; +} + +//--------------------------------------------------------- + +string TParamSet::getParamName(int i) const +{ + assert(i >= 0 && i < (int)m_imp->m_params.size()); + return m_imp->m_params[i].second; +} + +//--------------------------------------------------------- + +int TParamSet::getParamIdx(const string &name) const +{ + int i, paramsCount = m_imp->m_params.size(); + for (i = 0; i < paramsCount; ++i) + if (m_imp->m_params[i].second == name) + break; + + return i; +} + +//--------------------------------------------------------- + +void TParamSet::getAnimatableParams(vector ¶ms, bool recursive) +{ + std::vector>::iterator it = m_imp->m_params.begin(); + for (; it != m_imp->m_params.end(); ++it) { + TParam *param = it->first; + + TDoubleParamP dparam = TParamP(param); + if (dparam) + params.push_back(dparam); + else { + TParamSetP paramset = TParamP(param); + if (paramset && recursive) + paramset->getAnimatableParams(params, recursive); + } + } +} + +//--------------------------------------------------------- + +void TParamSet::addObserver(TParamObserver *observer) +{ + //TParamSetObserver *obs = dynamic_cast(observer); + //if (obs) + // m_imp->m_observers.insert(obs); + //else + m_imp->m_paramObservers.insert(observer); +} + +//--------------------------------------------------------- + +template +void TParamSetImp::notify(const T &change) +{ + if (m_notificationEnabled) { + // for (std::set::iterator it = m_observers.begin(); + // it!= m_observers.end(); + // ++it) + // (*it)->onChange(change); + for (std::set::iterator paramIt = m_paramObservers.begin(); + paramIt != m_paramObservers.end(); + ++paramIt) + (*paramIt)->onChange(change); + } +} + +//--------------------------------------------------------- + +void TParamSet::removeObserver(TParamObserver *observer) +{ + //TParamSetObserver *obs = dynamic_cast(observer); + //if (obs) + // m_imp->m_observers.erase(obs); + //else + m_imp->m_paramObservers.erase(observer); +} + +//--------------------------------------------------------- + +void TParamSet::enableDragging(bool on) +{ + std::vector>::iterator it = m_imp->m_params.begin(); + for (; it != m_imp->m_params.end(); ++it) { + TDoubleParamP dparam(it->first); + //if (dparam) + // dparam->enableDragging(on); + } + + m_imp->m_draggingEnabled = on; +} + +//--------------------------------------------------------- +/* +namespace { + +class DoEnableNotification : public std::binary_function { +public: + DoEnableNotification() {} + + void operator() (const pair ¶m, bool on) + { + return param->first->enableNotification(on); + } +}; +} +*/ +//--------------------------------------------------------- + +void TParamSet::enableNotification(bool on) +{ + // std::for_each(m_imp->m_params.begin(), m_imp->m_params.end(), std::bind2nd(DoEnableNotification, on)); + + std::vector>::iterator it = m_imp->m_params.begin(); + for (; it != m_imp->m_params.end(); ++it) { + it->first->enableNotification(on); + } + + m_imp->m_notificationEnabled = on; +} + +//--------------------------------------------------------- + +bool TParamSet::isNotificationEnabled() const +{ + return m_imp->m_notificationEnabled; +} + +//--------------------------------------------------------- + +bool TParamSet::isKeyframe(double frame) const +{ + for (int i = 0; i < getParamCount(); i++) + if (getParam(i)->isKeyframe(frame)) + return true; + return false; +} + +//--------------------------------------------------------- + +void TParamSet::getKeyframes(std::set &frames) const +{ + for (int i = 0; i < getParamCount(); i++) + getParam(i)->getKeyframes(frames); +} + +//--------------------------------------------------------- + +double TParamSet::keyframeIndexToFrame(int index) const +{ + std::set frames; + getKeyframes(frames); + assert(0 <= index && index < (int)frames.size()); + std::set::const_iterator it = frames.begin(); + std::advance(it, index); + return *it; +} + +//--------------------------------------------------------- + +int TParamSet::getNextKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.upper_bound(frame); + if (it == frames.end()) + return -1; + else + return std::distance(frames.begin(), it); +} + +//--------------------------------------------------------- + +int TParamSet::getPrevKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.lower_bound(frame); + if (it == frames.begin()) + return -1; + else { + --it; + return std::distance(frames.begin(), it); + } +} + +//--------------------------------------------------------- + +bool TParamSet::hasKeyframes() const +{ + for (int i = 0; i < getParamCount(); i++) + if (getParam(i)->hasKeyframes()) + return true; + return false; +} + +//--------------------------------------------------------- + +int TParamSet::getKeyframeCount() const +{ + std::set frames; + getKeyframes(frames); + return frames.size(); +} + +//--------------------------------------------------------- + +void TParamSet::deleteKeyframe(double frame) +{ + for (int i = 0; i < getParamCount(); i++) + getParam(i)->deleteKeyframe(frame); +} + +//--------------------------------------------------------- + +void TParamSet::clearKeyframes() +{ + for (int i = 0; i < getParamCount(); i++) + getParam(i)->clearKeyframes(); +} + +//--------------------------------------------------------- + +void TParamSet::assignKeyframe( + double frame, + const TParamP &src, double srcFrame, + bool changedOnly) +{ + TParamSetP paramSetSrc = src; + if (!paramSetSrc) + return; + if (getParamCount() != paramSetSrc->getParamCount()) + return; + for (int i = 0; i < getParamCount(); i++) + getParam(i)->assignKeyframe( + frame, paramSetSrc->getParam(i), srcFrame, changedOnly); +} + +//--------------------------------------------------------- + +TParam *TParamSet::clone() const +{ + return new TParamSet(*this); +} + +//--------------------------------------------------------- + +void TParamSet::copy(TParam *src) +{ + TParamSet *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + int srcParamCount = p->getParamCount(); + removeAllParam(); + int i; + for (i = 0; i < srcParamCount; i++) { + TParamP param = p->getParam(i); + addParam(param->clone(), param->getName()); + } +} + +//--------------------------------------------------------- + +void TParamSet::loadData(TIStream &is) +{ + string tagName; + is.openChild(tagName); + while (!is.eos()) { + string paramName; + is.openChild(paramName); + TPersist *p = 0; + is >> p; + TParam *param = dynamic_cast(p); + assert(param); + addParam(param, paramName); + is.closeChild(); + } + is.closeChild(); +} + +//--------------------------------------------------------- + +void TParamSet::saveData(TOStream &os) +{ + os.openChild(getName()); + std::vector>::iterator it = m_imp->m_params.begin(); + std::vector>::iterator end = m_imp->m_params.end(); + while (it != end) { + os.openChild(it->second); + //it->first->saveData(os); + os << it->first; + os.closeChild(); + ++it; + } + os.closeChild(); +} + +//--------------------------------------------------------- + +string TParamSet::getValueAlias(double frame, int precision) +{ + string alias = "("; + + std::vector>::iterator end = m_imp->m_params.begin(); + std::advance(end, m_imp->m_params.size() - 1); + + std::vector>::iterator it = m_imp->m_params.begin(); + for (; it != end; ++it) + alias += it->first->getValueAlias(frame, precision) + ","; + + alias += it->first->getValueAlias(frame, precision); + + alias += ")"; + return alias; +} + +//--------------------------------------------------------- + +TPersistDeclarationT TParamSet::m_declaration("TParamSet"); diff --git a/toonz/sources/common/tparam/tpixelparam.cpp b/toonz/sources/common/tparam/tpixelparam.cpp new file mode 100644 index 0000000..21bae5c --- /dev/null +++ b/toonz/sources/common/tparam/tpixelparam.cpp @@ -0,0 +1,244 @@ + + +//#include "tpixelparam.h" +#include "tparamset.h" +#include "tdoubleparam.h" +#include "texception.h" +#include "tpixelutils.h" +#include "tstream.h" + +class TPixelParamImp +{ +public: + TPixelParamImp(const TPixel32 &p) + : m_r(new TDoubleParam(p.r / 255.0)), m_g(new TDoubleParam(p.g / 255.0)), m_b(new TDoubleParam(p.b / 255.0)), m_m(new TDoubleParam(p.m / 255.0)), m_isMatteEnabled(true) + { + } + TPixelParamImp(const TPixelParamImp &src) + : m_r(src.m_r->clone()), m_g(src.m_g->clone()), m_b(src.m_b->clone()), m_m(src.m_m->clone()), m_isMatteEnabled(src.m_isMatteEnabled) + { + } + ~TPixelParamImp() {} + TDoubleParamP m_r, m_g, m_b, m_m; + bool m_isMatteEnabled; +}; + +PERSIST_IDENTIFIER(TPixelParam, "pixelParam") + +//--------------------------------------------------------- + +TPixelParam::TPixelParam(const TPixel32 &p) + : m_data(new TPixelParamImp(p)) +{ + addParam(m_data->m_r, "Red"); + addParam(m_data->m_g, "Green"); + addParam(m_data->m_b, "Blue"); + addParam(m_data->m_m, "Alpha"); + string measureName("colorChannel"); + m_data->m_r->setMeasureName(measureName); + m_data->m_g->setMeasureName(measureName); + m_data->m_b->setMeasureName(measureName); + m_data->m_m->setMeasureName(measureName); +} + +//--------------------------------------------------------- + +TPixelParam::TPixelParam(const TPixelParam &src) + : TParamSet(src.getName()), m_data(new TPixelParamImp(*src.m_data)) +{ + addParam(m_data->m_r, "Red"); + addParam(m_data->m_g, "Green"); + addParam(m_data->m_b, "Blue"); + addParam(m_data->m_m, "Alpha"); + string measureName("colorChannel"); + m_data->m_r->setMeasureName(measureName); + m_data->m_g->setMeasureName(measureName); + m_data->m_b->setMeasureName(measureName); + m_data->m_m->setMeasureName(measureName); +} + +//--------------------------------------------------------- + +void TPixelParam::copy(TParam *src) +{ + TPixelParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_data->m_r->copy(p->m_data->m_r.getPointer()); + m_data->m_g->copy(p->m_data->m_g.getPointer()); + m_data->m_b->copy(p->m_data->m_b.getPointer()); + m_data->m_m->copy(p->m_data->m_m.getPointer()); + string measureName("colorChannel"); + + m_data->m_r->setMeasureName(measureName); + m_data->m_g->setMeasureName(measureName); + m_data->m_b->setMeasureName(measureName); + m_data->m_m->setMeasureName(measureName); +} + +//--------------------------------------------------------- + +TPixelParam::~TPixelParam() +{ + delete m_data; +} + +//--------------------------------------------------------- + +TPixel32 TPixelParam::getDefaultValue() const +{ + TPixelD pixd( + m_data->m_r->getDefaultValue(), + m_data->m_g->getDefaultValue(), + m_data->m_b->getDefaultValue(), + m_data->m_m->getDefaultValue()); + return toPixel32(pixd); +} + +//--------------------------------------------------------- + +void TPixelParam::setDefaultValue(const TPixel32 &p) +{ + TPixelD pixd = toPixelD(p); + m_data->m_r->setDefaultValue(pixd.r); + m_data->m_g->setDefaultValue(pixd.g); + m_data->m_b->setDefaultValue(pixd.b); + m_data->m_m->setDefaultValue(pixd.m); +} + +//--------------------------------------------------------- + +TPixelD TPixelParam::getValueD(double frame) const +{ + return TPixelD(m_data->m_r->getValue(frame), m_data->m_g->getValue(frame), + m_data->m_b->getValue(frame), m_data->m_m->getValue(frame)); +} + +//--------------------------------------------------------- + +TPixel32 TPixelParam::getValue(double frame) const +{ + return toPixel32(getValueD(frame)); +} + +//--------------------------------------------------------- + +TPixel64 TPixelParam::getValue64(double frame) const +{ + return toPixel64(getValueD(frame)); +} + +//--------------------------------------------------------- + +TPixel32 TPixelParam::getPremultipliedValue(double frame) const +{ + return premultiply(getValue(frame)); +} + +//--------------------------------------------------------- + +bool TPixelParam::setValueD(double frame, const TPixelD &p) +{ + beginParameterChange(); + m_data->m_r->setValue(frame, p.r); + m_data->m_g->setValue(frame, p.g); + m_data->m_b->setValue(frame, p.b); + m_data->m_m->setValue(frame, p.m); + endParameterChange(); + return true; +} + +//--------------------------------------------------------- + +bool TPixelParam::setValue(double frame, const TPixel32 &pix) +{ + return setValueD(frame, toPixelD(pix)); +} + +//--------------------------------------------------------- + +bool TPixelParam::setValue64(double frame, const TPixel64 &pix) +{ + return setValueD(frame, toPixelD(pix)); +} + +//--------------------------------------------------------- + +void TPixelParam::loadData(TIStream &is) +{ + string childName; + while (is.openChild(childName)) { + if (childName == "red") + m_data->m_r->loadData(is); + else if (childName == "green") + m_data->m_g->loadData(is); + else if (childName == "blue") + m_data->m_b->loadData(is); + else if (childName == "matte") + m_data->m_m->loadData(is); + else + throw TException("unknown channel name: " + childName); + is.closeChild(); + } +} + +//--------------------------------------------------------- + +void TPixelParam::saveData(TOStream &os) +{ + os.openChild("red"); + m_data->m_r->saveData(os); + os.closeChild(); + os.openChild("green"); + m_data->m_g->saveData(os); + os.closeChild(); + os.openChild("blue"); + m_data->m_b->saveData(os); + os.closeChild(); + os.openChild("matte"); + m_data->m_m->saveData(os); + os.closeChild(); +} +//--------------------------------------------------------- + +TDoubleParamP &TPixelParam::getRed() +{ + return m_data->m_r; +} + +//--------------------------------------------------------- + +TDoubleParamP &TPixelParam::getGreen() +{ + return m_data->m_g; +} + +//--------------------------------------------------------- + +TDoubleParamP &TPixelParam::getBlue() +{ + return m_data->m_b; +} + +//--------------------------------------------------------- + +TDoubleParamP &TPixelParam::getMatte() +{ + return m_data->m_m; +} + +//--------------------------------------------------------- + +void TPixelParam::enableMatte(bool on) +{ + m_data->m_isMatteEnabled = on; + if (on == false) + m_data->m_m = new TDoubleParam(255.0); +} +//--------------------------------------------------------- + +bool TPixelParam::isMatteEnabled() const +{ + return m_data->m_isMatteEnabled; +} diff --git a/toonz/sources/common/tparam/tpointparam.cpp b/toonz/sources/common/tparam/tpointparam.cpp new file mode 100644 index 0000000..0055b71 --- /dev/null +++ b/toonz/sources/common/tparam/tpointparam.cpp @@ -0,0 +1,138 @@ + + +//#include "tpointparam.h" +#include "tparamset.h" +#include "tdoubleparam.h" +#include "texception.h" +#include "tstream.h" + +//========================================================= + +class TPointParamImp +{ +public: + TPointParamImp(const TPointD &p) + : m_x(new TDoubleParam(p.x)), m_y(new TDoubleParam(p.y)) + { + } + TPointParamImp(const TPointParamImp &src) + : m_x(src.m_x->clone()), m_y(src.m_y->clone()) + { + } + ~TPointParamImp() {} + TDoubleParamP m_x, m_y; +}; + +//--------------------------------------------------------- + +PERSIST_IDENTIFIER(TPointParam, "pointParam") + +TPointParam::TPointParam(const TPointD &p, bool from_plugin) + : m_data(new TPointParamImp(p)), m_from_plugin(from_plugin) +{ + addParam(m_data->m_x, "x"); + addParam(m_data->m_y, "y"); +} + +//--------------------------------------------------------- + +TPointParam::TPointParam(const TPointParam &src) + : TParamSet(src.getName()), m_data(new TPointParamImp(*src.m_data)), m_from_plugin(src.m_from_plugin) +{ + addParam(m_data->m_x, "x"); + addParam(m_data->m_y, "y"); +} + +//--------------------------------------------------------- + +TPointParam::~TPointParam() +{ + delete m_data; +} + +//--------------------------------------------------------- + +void TPointParam::copy(TParam *src) +{ + TPointParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_data->m_x->copy(p->m_data->m_x.getPointer()); + m_data->m_y->copy(p->m_data->m_y.getPointer()); +} + +//--------------------------------------------------------- + +TPointD TPointParam::getDefaultValue() const +{ + return TPointD(m_data->m_x->getDefaultValue(), m_data->m_y->getDefaultValue()); +} + +//--------------------------------------------------------- + +TPointD TPointParam::getValue(double frame) const +{ + return TPointD(m_data->m_x->getValue(frame), m_data->m_y->getValue(frame)); +} + +//--------------------------------------------------------- + +bool TPointParam::setValue(double frame, const TPointD &p) +{ + beginParameterChange(); + m_data->m_x->setValue(frame, p.x); + m_data->m_y->setValue(frame, p.y); + endParameterChange(); + return true; +} + +//--------------------------------------------------------- + +void TPointParam::setDefaultValue(const TPointD &p) +{ + m_data->m_x->setDefaultValue(p.x); + m_data->m_y->setDefaultValue(p.y); +} + +//--------------------------------------------------------- + +void TPointParam::loadData(TIStream &is) +{ + string childName; + while (is.openChild(childName)) { + if (childName == "x") + m_data->m_x->loadData(is); + else if (childName == "y") + m_data->m_y->loadData(is); + else + throw TException("unknown coord"); + is.closeChild(); + } +} + +//--------------------------------------------------------- + +void TPointParam::saveData(TOStream &os) +{ + os.openChild("x"); + m_data->m_x->saveData(os); + os.closeChild(); + os.openChild("y"); + m_data->m_y->saveData(os); + os.closeChild(); +} + +//--------------------------------------------------------- + +TDoubleParamP &TPointParam::getX() +{ + return m_data->m_x; +} + +//--------------------------------------------------------- + +TDoubleParamP &TPointParam::getY() +{ + return m_data->m_y; +} diff --git a/toonz/sources/common/tparam/trangeparam.cpp b/toonz/sources/common/tparam/trangeparam.cpp new file mode 100644 index 0000000..7f3a637 --- /dev/null +++ b/toonz/sources/common/tparam/trangeparam.cpp @@ -0,0 +1,188 @@ + + +#include "tparamset.h" +#include "tdoubleparam.h" +#include "texception.h" +#include "tstream.h" + +//========================================================= + +class TRangeParamImp +{ +public: + TRangeParamImp(const DoublePair &v) + : m_min(new TDoubleParam(v.first)), m_max(new TDoubleParam(v.second)) + { + } + TRangeParamImp(const TRangeParamImp &src) + : m_min(src.m_min->clone()), m_max(src.m_max->clone()) + { + } + + ~TRangeParamImp() {} + + TDoubleParamP m_min, m_max; +}; + +//--------------------------------------------------------- + +PERSIST_IDENTIFIER(TRangeParam, "rangeParam") + +TRangeParam::TRangeParam(const DoublePair &v) + : m_data(new TRangeParamImp(v)) +{ + addParam(m_data->m_min, "min"); + addParam(m_data->m_max, "max"); +} + +//--------------------------------------------------------- + +TRangeParam::TRangeParam(const TRangeParam &src) + : TParamSet(src.getName()), m_data(new TRangeParamImp(*src.m_data)) +{ + addParam(m_data->m_min, "min"); + addParam(m_data->m_max, "max"); +} + +//--------------------------------------------------------- + +TRangeParam::~TRangeParam() +{ + delete m_data; +} + +//--------------------------------------------------------- + +void TRangeParam::copy(TParam *src) +{ + TRangeParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_data->m_min->copy(p->m_data->m_min.getPointer()); + m_data->m_max->copy(p->m_data->m_max.getPointer()); +} + +//--------------------------------------------------------- + +DoublePair TRangeParam::getDefaultValue() const +{ + return DoublePair(m_data->m_min->getDefaultValue(), m_data->m_max->getDefaultValue()); +} + +//--------------------------------------------------------- + +DoublePair TRangeParam::getValue(double frame) const +{ + return DoublePair(m_data->m_min->getValue(frame), m_data->m_max->getValue(frame)); +} + +//--------------------------------------------------------- + +bool TRangeParam::setValue(double frame, const DoublePair &v) +{ + beginParameterChange(); + m_data->m_min->setValue(frame, v.first); + m_data->m_max->setValue(frame, v.second); + endParameterChange(); + return true; +} + +//--------------------------------------------------------- + +void TRangeParam::setDefaultValue(const DoublePair &v) +{ + m_data->m_min->setDefaultValue(v.first); + m_data->m_max->setDefaultValue(v.second); +} + +//--------------------------------------------------------- + +void TRangeParam::loadData(TIStream &is) +{ + string childName; + while (is.openChild(childName)) { + if (childName == "min") + m_data->m_min->loadData(is); + else if (childName == "max") + m_data->m_max->loadData(is); + else + throw TException("unknown tag"); + is.closeChild(); + } +} + +//--------------------------------------------------------- + +void TRangeParam::saveData(TOStream &os) +{ + os.openChild("min"); + m_data->m_min->saveData(os); + os.closeChild(); + os.openChild("max"); + m_data->m_max->saveData(os); + os.closeChild(); +} + +//--------------------------------------------------------- + +TDoubleParamP &TRangeParam::getMin() +{ + return m_data->m_min; +} + +//--------------------------------------------------------- + +TDoubleParamP &TRangeParam::getMax() +{ + return m_data->m_max; +} +#ifdef BUTTA +//--------------------------------------------------------- + +int TRangeParam::getNextKeyframe(double frame) const +{ + int f_min = m_data->m_min->getNextKeyframe(frame); + int f_max = m_data->m_max->getNextKeyframe(frame); + if (f_min <= f_max && f_min != -1) + return f_min; + else { + if (f_max != -1) + return f_max; + else + return -1; + } +} + +//--------------------------------------------------------- + +int TRangeParam::getPrevKeyframe(double frame) const +{ + int f_min = m_data->m_min->getPrevKeyframe(frame); + int f_max = m_data->m_max->getPrevKeyframe(frame); + if (f_min >= f_max) + return f_min; + else + return f_max; +} + +//--------------------------------------------------------- + +void TRangeParam::deleteKeyframe(double frame, bool undoing) +{ + m_data->m_min->deleteKeyframe(frame, undoing); + m_data->m_max->deleteKeyframe(frame, undoing); +} + +//--------------------------------------------------------- + +bool TRangeParam::isKeyframe(double frame) const +{ + bool min, max; + min = m_data->m_min->isKeyframe(frame); + max = m_data->m_max->isKeyframe(frame); + return (min || max); +} +//--------------------------------------------------------- + +#endif diff --git a/toonz/sources/common/tparam/tspectrumparam.cpp b/toonz/sources/common/tparam/tspectrumparam.cpp new file mode 100644 index 0000000..a55a88b --- /dev/null +++ b/toonz/sources/common/tparam/tspectrumparam.cpp @@ -0,0 +1,603 @@ + + +// TnzCore includes +#include "tundo.h" +#include "tconvert.h" +#include "tstream.h" + +// TnzBase includes +#include "tdoubleparam.h" + +// STD includes +#include + +#include "tspectrumparam.h" + +typedef pair ColorKeyParam; + +//========================================================= + +class TSpectrumParamImp +{ + TSpectrumParam *m_sp; + + std::vector m_keys; + +public: + bool m_draggingEnabled; + bool m_notificationEnabled; + bool m_isMatteEnabled; + + std::set m_observers; + +public: + TSpectrumParamImp(TSpectrumParam *sp) : m_sp(sp), m_keys(), m_draggingEnabled(false), m_notificationEnabled(true), m_isMatteEnabled(true) + { + } + + TSpectrumParamImp(const TSpectrumParamImp &s) + { + copy(s); + } + + void copy(const TSpectrumParamImp &src) + { + m_keys.clear(); + + std::vector::const_iterator it = src.m_keys.begin(); + for (; it != src.m_keys.end(); ++it) { + TDoubleParamP s(it->first->clone()); + TPixelParamP c(it->second->clone()); + m_keys.push_back(std::make_pair(s, c)); + } + } + + void addKey(const ColorKeyParam &colorKey) + { + /* + m_sp->addParam(colorKey.first); + m_sp->addParam(colorKey.second); +*/ + m_keys.push_back(colorKey); + } + + void insertKey(int index, ColorKeyParam &colorKey) + { + /* + m_sp->addParam(colorKey.first); + m_sp->addParam(colorKey.second); +*/ + std::vector::iterator it = m_keys.begin() + index; + m_keys.insert(it, colorKey); + } + + void eraseKey(int index) + { + std::vector::iterator colorKeyIt = m_keys.begin() + index; + /* + m_sp->removeParam((*colorKeyIt).first); + m_sp->removeParam((*colorKeyIt).second); +*/ + m_keys.erase(colorKeyIt); + } + + int getKeyCount() const + { + return m_keys.size(); + } + + ColorKeyParam getKey(int index) const + { + return m_keys[index]; + } + + void clearKeys() + { + m_keys.clear(); + } + + void notify(const TParamChange &change) + { + for (std::set::iterator it = m_observers.begin(); + it != m_observers.end(); ++it) + (*it)->onChange(change); + } + +private: + TSpectrumParamImp &operator=(const TSpectrumParamImp &); //not implemented +}; + +//========================================================= + +PERSIST_IDENTIFIER(TSpectrumParam, "spectrumParam") + +//--------------------------------------------------------- + +TSpectrumParam::TSpectrumParam() +{ + m_imp = new TSpectrumParamImp(this); //brutto... + ColorKeyParam ck1(TDoubleParamP(0.0), TPixelParamP(TPixel32::Black)); + ColorKeyParam ck2(TDoubleParamP(1.0), TPixelParamP(TPixel32::White)); + m_imp->addKey(ck1); + m_imp->addKey(ck2); +} + +//--------------------------------------------------------- + +TSpectrumParam::TSpectrumParam(const TSpectrumParam &src) + : TParam(src.getName()) +{ + m_imp = new TSpectrumParamImp(*src.m_imp); +} + +//--------------------------------------------------------- + +void TSpectrumParam::addObserver(TParamObserver *obs) +{ + m_imp->m_observers.insert(obs); +} + +//--------------------------------------------------------- + +void TSpectrumParam::removeObserver(TParamObserver *obs) +{ + m_imp->m_observers.erase(obs); +} + +//--------------------------------------------------------- + +TSpectrumParam::TSpectrumParam(int keyCount, TSpectrum::ColorKey keys[]) +{ + m_imp = new TSpectrumParamImp(this); + for (int i = 0; i < keyCount; i++) { + double v = keys[i].first; + TPixel32 pix = keys[i].second; + TDoubleParamP dp(v); + TPixelParamP pp(pix); + pp->enableMatte(m_imp->m_isMatteEnabled); + ColorKeyParam ck(dp, pp); + m_imp->addKey(ck); + } +} + +//--------------------------------------------------------- + +void TSpectrumParam::copy(TParam *src) +{ + TSpectrumParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_imp->copy(*(p->m_imp)); +} + +//--------------------------------------------------------- + +TSpectrumParam::~TSpectrumParam() +{ + delete m_imp; +} + +//--------------------------------------------------------- + +TSpectrum TSpectrumParam::getValue(double frame) const +{ + assert(m_imp); + std::vector keys; + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam paramKey = m_imp->getKey(i); + TSpectrum::ColorKey key( + paramKey.first->getValue(frame), + paramKey.second->getValue(frame)); + keys.push_back(key); + } + return TSpectrum(keys.size(), &keys[0]); +} + +//--------------------------------------------------------- + +TSpectrum64 TSpectrumParam::getValue64(double frame) const +{ + assert(m_imp); + std::vector keys; + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam paramKey = m_imp->getKey(i); + TSpectrum64::ColorKey key( + paramKey.first->getValue(frame), + toPixel64(paramKey.second->getValue(frame))); + keys.push_back(key); + } + return TSpectrum64(keys.size(), &keys[0]); +} +//--------------------------------------------------------- + +void TSpectrumParam::setValue(double frame, const TSpectrum &spectrum, bool undoing) +{ + assert(getKeyCount() == spectrum.getKeyCount()); + int keyCount = getKeyCount(); + for (int i = 0; i < keyCount; i++) { + TSpectrum::Key key = spectrum.getKey(i); + setValue(frame, i, key.first, key.second, undoing); + } +} + +//--------------------------------------------------------- + +TDoubleParamP TSpectrumParam::getPosition(int index) const +{ + assert(index <= m_imp->getKeyCount()); + return m_imp->getKey(index).first; +} + +//--------------------------------------------------------- + +TPixelParamP TSpectrumParam::getColor(int index) const +{ + assert(index <= m_imp->getKeyCount()); + return m_imp->getKey(index).second; +} + +//--------------------------------------------------------- + +int TSpectrumParam::getKeyCount() const +{ + assert(m_imp); + return m_imp->getKeyCount(); +} + +//--------------------------------------------------------- + +void TSpectrumParam::setValue(double frame, int index, double s, const TPixel32 &color, bool undoing) +{ + assert(m_imp); + int keyCount = m_imp->getKeyCount(); + if (index < 0 || index >= keyCount) + throw TException("TSpectrumParam::setValue. Index out of range"); + + ColorKeyParam key = m_imp->getKey(index); + + // beginParameterChange(); + key.first->setValue(frame, s); + key.second->setValue(frame, color); + // endParameterChange(); + + m_imp->notify(TParamChange(this, TParamChange::m_minFrame, TParamChange::m_maxFrame, true, m_imp->m_draggingEnabled, false)); +} + +//--------------------------------------------------------- + +void TSpectrumParam::setDefaultValue(const TSpectrum &value) +{ + assert(value.getKeyCount() == getKeyCount()); + for (int i = 0; i < getKeyCount(); i++) { + ColorKeyParam dstKeyParam = m_imp->getKey(i); + TSpectrum::Key srcKey = value.getKey(i); + dstKeyParam.first->setDefaultValue(srcKey.first); + dstKeyParam.second->setDefaultValue(srcKey.second); + } +} + +//--------------------------------------------------------- + +void TSpectrumParam::insertKey(int index, double s, const TPixel32 &color) +{ + assert(m_imp); + int keyCount = m_imp->getKeyCount(); + if (index < 0) + index = 0; + else if (index >= keyCount) + index = keyCount; + TDoubleParamP dp(s); + TPixelParamP pp(color); + pp->enableMatte(m_imp->m_isMatteEnabled); + ColorKeyParam ck(dp, pp); + + m_imp->insertKey(index, ck); +} + +//--------------------------------------------------------- + +void TSpectrumParam::addKey(double s, const TPixel32 &color) +{ + /* + assert(m_imp); + insertKey(m_imp->getKeyCount(), s,color); +*/ + int index = m_imp->getKeyCount(); + assert(m_imp); + int keyCount = m_imp->getKeyCount(); + if (index < 0) + index = 0; + else if (index >= keyCount) + index = keyCount; + TDoubleParamP dp(s); + TPixelParamP pp(color); + pp->enableMatte(m_imp->m_isMatteEnabled); + ColorKeyParam ck(dp, pp); + + m_imp->insertKey(index, ck); +} + +//--------------------------------------------------------- + +void TSpectrumParam::removeKey(int index) +{ + assert(m_imp); + int keyCount = m_imp->getKeyCount(); + if (index < 0 || index >= keyCount) + throw TException("TSpectrumParam::removeKey. Index out of range"); + m_imp->eraseKey(index); +} + +//--------------------------------------------------------- + +bool TSpectrumParam::isKeyframe(double frame) const +{ + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam currentKey = m_imp->getKey(i); + if (currentKey.first->isKeyframe(frame)) + return true; + if (currentKey.second->isKeyframe(frame)) + return true; + } + return false; +} + +//--------------------------------------------------------- + +void TSpectrumParam::deleteKeyframe(double frame) +{ + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam currentKey = m_imp->getKey(i); + currentKey.first->deleteKeyframe(frame); + currentKey.second->deleteKeyframe(frame); + } +} + +//--------------------------------------------------------- + +void TSpectrumParam::clearKeyframes() +{ + assert(m_imp); + + int k, keyCount = m_imp->getKeyCount(); + for (k = 0; k < keyCount; ++k) { + const ColorKeyParam &key = m_imp->getKey(k); + + key.first->clearKeyframes(); + key.second->clearKeyframes(); + } + + m_imp->notify(TParamChange(this, TParamChange::m_minFrame, TParamChange::m_maxFrame, true, m_imp->m_draggingEnabled, false)); +} + +//--------------------------------------------------------- + +void TSpectrumParam::assignKeyframe( + double frame, + const TParamP &src, double srcFrame, + bool changedOnly) +{ + TSpectrumParamP spectrum = src; + if (!spectrum) + return; + int keyCount = m_imp->getKeyCount(); + if (keyCount != spectrum->m_imp->getKeyCount()) + return; + for (int i = 0; i < keyCount; i++) { + ColorKeyParam dstKey = m_imp->getKey(i); + ColorKeyParam srcKey = spectrum->m_imp->getKey(i); + dstKey.first->setValue(frame, srcKey.first->getValue(srcFrame)); + dstKey.second->setValue(frame, srcKey.second->getValue(srcFrame)); + } +} + +//--------------------------------------------------------- + +void TSpectrumParam::loadData(TIStream &is) +{ + assert(m_imp); + m_imp->clearKeys(); + string tagName; + is.openChild(tagName); + assert(tagName == "spectrum"); + while (!is.eos()) { + TDoubleParamP pos(0.0); + TPixelParamP color(TPixel32::Black); + is.openChild(tagName); + pos->loadData(is); + is.closeChild(); + is.openChild(tagName); + color->loadData(is); + is.closeChild(); + ColorKeyParam ck(pos, color); + m_imp->addKey(ck); + } + is.closeChild(); +} + +//--------------------------------------------------------- + +void TSpectrumParam::saveData(TOStream &os) +{ + assert(m_imp); + int keyCount = m_imp->getKeyCount(); + os.openChild("spectrum"); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam key = m_imp->getKey(i); + os.openChild("s_value"); + key.first->saveData(os); + os.closeChild(); + os.openChild("col_value"); + key.second->saveData(os); + os.closeChild(); + } + os.closeChild(); +} + +//--------------------------------------------------------- + +void TSpectrumParam::enableDragging(bool on) +{ + m_imp->m_draggingEnabled = on; +} + +//--------------------------------------------------------- + +void TSpectrumParam::enableNotification(bool on) +{ + m_imp->m_notificationEnabled = on; +} + +//--------------------------------------------------------- + +bool TSpectrumParam::isNotificationEnabled() const +{ + return m_imp->m_notificationEnabled; +} + +//--------------------------------------------------------- + +namespace +{ + +inline string toString(const TPixel32 &color) +{ + string alias = "("; + alias += ::toString(color.r) + ","; + alias += ::toString(color.g) + ","; + alias += ::toString(color.b) + ","; + alias += ::toString(color.m); + alias += ")"; + return alias; +} + +inline string toString(const TSpectrum::ColorKey &key, int precision) +{ + string alias = "("; + alias += ::toString(key.first, precision) + ","; + alias += toString(key.second); + alias += ")"; + return alias; +} + +} // namespace + +string TSpectrumParam::getValueAlias(double frame, int precision) +{ + std::vector keys; + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam paramKey = m_imp->getKey(i); + TSpectrum::ColorKey key( + paramKey.first->getValue(frame), + paramKey.second->getValue(frame)); + keys.push_back(key); + } + + string alias = "("; + + if (!keys.empty()) { + std::vector::iterator it = keys.begin(); + std::vector::iterator end = keys.begin(); + std::advance(end, keys.size() - 1); + for (; it != end; ++it) { + alias += toString(*it, precision); + alias += ","; + } + alias += toString(*it, precision); + } + + alias += ")"; + return alias; +} + +//========================================================= + +//--------------------------------------------------------- + +void TSpectrumParam::enableMatte(bool on) +{ + m_imp->m_isMatteEnabled = on; +} +//--------------------------------------------------------- + +bool TSpectrumParam::isMatteEnabled() const +{ + return m_imp->m_isMatteEnabled; +} + +//--------------------------------------------------------- + +bool TSpectrumParam::hasKeyframes() const +{ + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam currentKey = m_imp->getKey(i); + if (currentKey.first->hasKeyframes() || currentKey.second->hasKeyframes()) + return true; + } + return false; +} +//--------------------------------------------------------- + +void TSpectrumParam::getKeyframes(std::set &frames) const +{ + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam currentKey = m_imp->getKey(i); + currentKey.first->getKeyframes(frames); + currentKey.second->getKeyframes(frames); + } +} + +//--------------------------------------------------------- + +int TSpectrumParam::getNextKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.upper_bound(frame); + if (it == frames.end()) + return -1; + else + return std::distance(frames.begin(), it); +} + +//--------------------------------------------------------- + +int TSpectrumParam::getPrevKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.lower_bound(frame); + if (it == frames.begin()) + return -1; + else { + --it; + return std::distance(frames.begin(), it); + } +} +//--------------------------------------------------------- + +double TSpectrumParam::keyframeIndexToFrame(int index) const +{ + std::set frames; + getKeyframes(frames); + assert(0 <= index && index < (int)frames.size()); + std::set::const_iterator it = frames.begin(); + std::advance(it, index); + return *it; +} + +TIStream &operator>>(TIStream &in, TSpectrumParamP &p) +{ + TPersist *tmp; + in >> tmp; + p = TSpectrumParamP(dynamic_cast(tmp)); + return in; +} diff --git a/toonz/sources/common/tparam/tstepparam.cpp b/toonz/sources/common/tparam/tstepparam.cpp new file mode 100644 index 0000000..1167ba5 --- /dev/null +++ b/toonz/sources/common/tparam/tstepparam.cpp @@ -0,0 +1,48 @@ + + +#include "tstepparam.h" + +// +// OBSOLETO?? +// + +/* + +PERSIST_IDENTIFIER(TDoubleStepParam, "doubleStepParam") + +//------------------------------------------------------------------------------ + +TDoubleStepParam::TDoubleStepParam(double v) + : TDoubleParam(v) +{} + +//------------------------------------------------------------------------------ + +TDoubleStepParam::TDoubleStepParam(const TDoubleParam &src) + : TDoubleParam(src) +{} + +//------------------------------------------------------------------------------ + +TDoubleStepParam::~TDoubleStepParam() +{} + +//------------------------------------------------------------------------------ + +double TDoubleStepParam::getValue(double frame, bool cropped) const +{ + if (getKeyframeCount() == 0) + return getDefaultValue(); + + if(isKeyframe(frame)) + return TDoubleParam::getValue(frame); + + int index = getPrevKeyframe(frame); + if (index != -1) + return getKeyframe(index).m_value; + else + return getKeyframe(0).m_value; +} + +//------------------------------------------------------------------------------ +*/ diff --git a/toonz/sources/common/tparam/ttonecurveparam.cpp b/toonz/sources/common/tparam/ttonecurveparam.cpp new file mode 100644 index 0000000..f1aee47 --- /dev/null +++ b/toonz/sources/common/tparam/ttonecurveparam.cpp @@ -0,0 +1,396 @@ + + +#include "ttonecurveparam.h" +#include "texception.h" +#include "tstream.h" + +#include + +//========================================================= + +PERSIST_IDENTIFIER(TToneCurveParam, "toneCurveParam") + +//--------------------------------------------------------- + +TToneCurveParam::TToneCurveParam() + : TParam() +{ + m_toneChannel = RGBA; + + std::vector points; + //Inserisco dei punti fuori dal range(0-255) perche' mi consentono di gestire i primi punti come speciali. + points.push_back(TPointD(-40, 0)); + points.push_back(TPointD(-20, 0)); + points.push_back(TPointD(-20, 0)); + points.push_back(TPointD(0, 0)); + points.push_back(TPointD(16, 16)); + points.push_back(TPointD(239, 239)); + points.push_back(TPointD(255, 255)); + points.push_back(TPointD(275, 255)); + points.push_back(TPointD(275, 255)); + points.push_back(TPointD(295, 255)); + m_rgbaParamSet = new TParamSet("redgreenbluealphachannel"); + m_rgbParamSet = new TParamSet("redgreenbluechannel"); + m_rParamSet = new TParamSet("redchannel"); + m_gParamSet = new TParamSet("greenchannel"); + m_bParamSet = new TParamSet("bluechannel"); + m_aParamSet = new TParamSet("alphachannel"); + + m_isLinear = new TBoolParam(false); + + int i; + for (i = 0; i < (int)points.size(); i++) { + m_rgbaParamSet->addParam(new TPointParam(points[i]), "point"); + m_rgbParamSet->addParam(new TPointParam(points[i]), "point"); + m_rParamSet->addParam(new TPointParam(points[i]), "point"); + m_gParamSet->addParam(new TPointParam(points[i]), "point"); + m_bParamSet->addParam(new TPointParam(points[i]), "point"); + m_aParamSet->addParam(new TPointParam(points[i]), "point"); + } +} + +//--------------------------------------------------------- + +TParamSetP getClonedParamSet(TParamSetP srcParamSet) +{ + TParamSetP dstParamSet = new TParamSet(srcParamSet->getName()); + int i; + for (i = 0; i < srcParamSet->getParamCount(); i++) { + TParamP param = srcParamSet->getParam(i); + dstParamSet->addParam(param->clone(), param->getName()); + } + return dstParamSet; +} + +//--------------------------------------------------------- + +TToneCurveParam::TToneCurveParam(const TToneCurveParam &src) + : TParam(src.getName()) +{ + m_rgbaParamSet = getClonedParamSet(src.getParamSet(RGBA)); + m_rgbParamSet = getClonedParamSet(src.getParamSet(RGB)); + m_rParamSet = getClonedParamSet(src.getParamSet(Red)); + m_gParamSet = getClonedParamSet(src.getParamSet(Green)); + m_bParamSet = getClonedParamSet(src.getParamSet(Blue)); + m_aParamSet = getClonedParamSet(src.getParamSet(Alpha)); + m_toneChannel = src.getCurrentChannel(); + m_isLinear = src.getIsLinearParam()->clone(); +} + +//--------------------------------------------------------- + +void TToneCurveParam::copy(TParam *src) +{ + TToneCurveParam *p = dynamic_cast(src); + if (!p) + throw TException("invalid source for copy"); + setName(src->getName()); + m_rgbaParamSet->copy(p->getParamSet(RGBA).getPointer()); + m_rgbParamSet->copy(p->getParamSet(RGB).getPointer()); + m_rParamSet->copy(p->getParamSet(Red).getPointer()); + m_gParamSet->copy(p->getParamSet(Green).getPointer()); + m_bParamSet->copy(p->getParamSet(Blue).getPointer()); + m_aParamSet->copy(p->getParamSet(Alpha).getPointer()); + m_isLinear->copy(p->getIsLinearParam().getPointer()); + m_toneChannel = p->getCurrentChannel(); +} + +//--------------------------------------------------------- + +void TToneCurveParam::addObserver(TParamObserver *observer) +{ + m_rgbaParamSet->addObserver(observer); + m_rgbParamSet->addObserver(observer); + m_rParamSet->addObserver(observer); + m_gParamSet->addObserver(observer); + m_bParamSet->addObserver(observer); + m_aParamSet->addObserver(observer); + m_isLinear->addObserver(observer); +} + +//--------------------------------------------------------- + +void TToneCurveParam::removeObserver(TParamObserver *observer) +{ + m_rgbaParamSet->removeObserver(observer); + m_rgbParamSet->removeObserver(observer); + m_rParamSet->removeObserver(observer); + m_gParamSet->removeObserver(observer); + m_bParamSet->removeObserver(observer); + m_aParamSet->removeObserver(observer); + m_isLinear->removeObserver(observer); +} + +//--------------------------------------------------------- + +TParamSetP TToneCurveParam::getParamSet(ToneChannel channel) const +{ + if (channel == RGBA) + return m_rgbaParamSet; + else if (channel == RGB) + return m_rgbParamSet; + else if (channel == Red) + return m_rParamSet; + else if (channel == Green) + return m_gParamSet; + else if (channel == Blue) + return m_bParamSet; + else if (channel == Alpha) + return m_aParamSet; + + return 0; +} + +//--------------------------------------------------------- + +TParamSetP TToneCurveParam::getCurrentParamSet() const +{ + return getParamSet(m_toneChannel); +} + +//--------------------------------------------------------- + +void TToneCurveParam::setCurrentChannel(ToneChannel channel) +{ + m_toneChannel = channel; +} + +//--------------------------------------------------------- + +QList TToneCurveParam::getValue(double frame) const +{ + int i; + QList points; + for (i = 0; i < getCurrentParamSet()->getParamCount(); i++) { + TPointParamP pointParam = getCurrentParamSet()->getParam(i); + points.push_back(pointParam->getValue(frame)); + } + return points; +} + +//--------------------------------------------------------- + +void TToneCurveParam::setValue(double frame, const QList &value, bool undoing) +{ + if (value.size() == 0) + return; + int paramCount = getCurrentParamSet()->getParamCount(); + assert(paramCount == value.size()); + int i = 0; + for (i = 0; i < paramCount; i++) { + TPointParamP param = getCurrentParamSet()->getParam(i); + TPointD point = value.at(i); + param->setValue(frame, point); + } +} + +//--------------------------------------------------------- + +bool TToneCurveParam::isLinear() const +{ + return m_isLinear->getValue(); +} + +//--------------------------------------------------------- + +void TToneCurveParam::setIsLinear(bool isLinear) +{ + m_isLinear->setValue(isLinear); +} + +//--------------------------------------------------------- + +void TToneCurveParam::addValue(double frame, const QList &value, int index) +{ + getCurrentParamSet()->insertParam(new TPointParam(value.at(index - 1)), "point", index - 1); + getCurrentParamSet()->insertParam(new TPointParam(value.at(index)), "point", index); + getCurrentParamSet()->insertParam(new TPointParam(value.at(index + 1)), "point", index + 1); +} + +//--------------------------------------------------------- + +void TToneCurveParam::removeValue(double frame, int index) +{ + getCurrentParamSet()->removeParam(getCurrentParamSet()->getParam(index - 1)); + getCurrentParamSet()->removeParam(getCurrentParamSet()->getParam(index - 1)); + getCurrentParamSet()->removeParam(getCurrentParamSet()->getParam(index - 1)); +} + +//--------------------------------------------------------- + +void TToneCurveParam::setDefaultValue(const QList &value) +{ + int pointCount = value.size(); + if (pointCount == 0) + return; + + int paramCount = getCurrentParamSet()->getParamCount(); + assert(paramCount == pointCount); + + int i; + for (i = 0; i < pointCount; i++) { + TPointParamP param = getCurrentParamSet()->getParam(i); + TPointD paramPoint(param->getValue(0)); + TPointD strokePoint(value.at(i)); + param->setDefaultValue(strokePoint); + } + m_isLinear->setDefaultValue(false); +} + +//--------------------------------------------------------- + +string TToneCurveParam::getValueAlias(double frame, int precision) +{ + return getCurrentParamSet()->getValueAlias(frame, precision) + m_isLinear->getValueAlias(frame, precision); +} + +//--------------------------------------------------------- + +bool TToneCurveParam::isKeyframe(double frame) const +{ + if (m_rgbaParamSet->isKeyframe(frame) || m_rgbParamSet->isKeyframe(frame) || m_rParamSet->isKeyframe(frame) || + m_gParamSet->isKeyframe(frame) || m_bParamSet->isKeyframe(frame) || m_aParamSet->isKeyframe(frame)) + return true; + return false; +} + +//--------------------------------------------------------- + +void TToneCurveParam::deleteKeyframe(double frame) +{ + m_rgbaParamSet->deleteKeyframe(frame); + m_rgbParamSet->deleteKeyframe(frame); + m_rParamSet->deleteKeyframe(frame); + m_gParamSet->deleteKeyframe(frame); + m_bParamSet->deleteKeyframe(frame); + m_aParamSet->deleteKeyframe(frame); +} + +//--------------------------------------------------------- + +void TToneCurveParam::clearKeyframes() +{ + m_rgbaParamSet->clearKeyframes(); + m_rgbParamSet->clearKeyframes(); + m_rParamSet->clearKeyframes(); + m_gParamSet->clearKeyframes(); + m_bParamSet->clearKeyframes(); + m_aParamSet->clearKeyframes(); +} + +//--------------------------------------------------------- + +void TToneCurveParam::assignKeyframe(double frame, const TSmartPointerT &src, + double srcFrame, bool changedOnly) +{ + m_rgbaParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); + m_rgbParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); + m_rParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); + m_gParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); + m_bParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); + m_aParamSet->assignKeyframe(frame, src, srcFrame, changedOnly); +} + +//--------------------------------------------------------- + +void TToneCurveParam::getKeyframes(std::set &frames) const +{ + m_rgbaParamSet->getKeyframes(frames); + m_rgbParamSet->getKeyframes(frames); + m_rParamSet->getKeyframes(frames); + m_gParamSet->getKeyframes(frames); + m_bParamSet->getKeyframes(frames); + m_aParamSet->getKeyframes(frames); +} + +//--------------------------------------------------------- + +bool TToneCurveParam::hasKeyframes() const +{ + if (m_rgbaParamSet->hasKeyframes() || m_rgbParamSet->hasKeyframes() || m_rParamSet->hasKeyframes() || + m_gParamSet->hasKeyframes() || m_bParamSet->hasKeyframes() || m_aParamSet->hasKeyframes()) + return true; + return false; +} + +//--------------------------------------------------------- + +int TToneCurveParam::getNextKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.upper_bound(frame); + if (it == frames.end()) + return -1; + else + return std::distance(frames.begin(), it); +} + +//--------------------------------------------------------- + +int TToneCurveParam::getPrevKeyframe(double frame) const +{ + std::set frames; + getKeyframes(frames); + std::set::iterator it = frames.lower_bound(frame); + if (it == frames.begin()) + return -1; + else { + --it; + return std::distance(frames.begin(), it); + } +} + +//--------------------------------------------------------- + +double TToneCurveParam::keyframeIndexToFrame(int index) const +{ + std::set frames; + getKeyframes(frames); + assert(0 <= index && index < (int)frames.size()); + std::set::const_iterator it = frames.begin(); + std::advance(it, index); + return *it; +} + +//--------------------------------------------------------- + +void TToneCurveParam::loadData(TIStream &is) +{ + string tagName; + is.openChild(tagName); + assert(tagName == "tonecurve"); + m_rgbaParamSet->removeAllParam(); + m_rgbaParamSet->loadData(is); + m_rgbParamSet->removeAllParam(); + m_rgbParamSet->loadData(is); + m_rParamSet->removeAllParam(); + m_rParamSet->loadData(is); + m_gParamSet->removeAllParam(); + m_gParamSet->loadData(is); + m_bParamSet->removeAllParam(); + m_bParamSet->loadData(is); + m_aParamSet->removeAllParam(); + m_aParamSet->loadData(is); + is.openChild(tagName); + m_isLinear->loadData(is); + is.closeChild(); + is.closeChild(); +} + +//--------------------------------------------------------- + +void TToneCurveParam::saveData(TOStream &os) +{ + os.openChild("tonecurve"); + m_rgbaParamSet->saveData(os); + m_rgbParamSet->saveData(os); + m_rParamSet->saveData(os); + m_gParamSet->saveData(os); + m_bParamSet->saveData(os); + m_aParamSet->saveData(os); + os.openChild("isLineaer"); + m_isLinear->saveData(os); + os.closeChild(); + os.closeChild(); +} diff --git a/toonz/sources/common/tproperty.cpp b/toonz/sources/common/tproperty.cpp new file mode 100644 index 0000000..4a87d7e --- /dev/null +++ b/toonz/sources/common/tproperty.cpp @@ -0,0 +1,319 @@ + + +#include "tproperty.h" +#include "tstream.h" +#include "texception.h" +//#include "tconvert.h" + +void TProperty::addListener(Listener *listener) +{ + if (std::find(m_listeners.begin(), m_listeners.end(), listener) == m_listeners.end()) + m_listeners.push_back(listener); +} + +void TProperty::removeListener(Listener *listener) +{ + m_listeners.erase(std::remove(m_listeners.begin(), m_listeners.end(), listener), m_listeners.end()); +} + +void TProperty::notifyListeners() const +{ + std::vector::const_iterator it; + for (it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->onPropertyChanged(); +} + +//============================================================================= + +TPropertyGroup::TPropertyGroup() +{ +} + +TPropertyGroup::~TPropertyGroup() +{ + for (PropertyVector::iterator it = m_properties.begin(); + it != m_properties.end(); ++it) + if (it->second) + delete it->first; +} + +void TPropertyGroup::clear() +{ + m_properties.clear(); + m_table.clear(); +} + +TPropertyGroup *TPropertyGroup::clone() const +{ + TPropertyGroup *g = new TPropertyGroup(); + for (PropertyVector::const_iterator i = m_properties.begin(); + i != m_properties.end(); ++i) + g->add(i->first->clone()); + return g; +} + +void TPropertyGroup::add(TProperty *p) +{ + string name = p->getName(); + assert(m_table.find(name) == m_table.end()); + m_properties.push_back(std::make_pair(p, true)); + m_table[name] = p; +} + +void TPropertyGroup::bind(TProperty &p) +{ + string name = p.getName(); + assert(m_table.find(name) == m_table.end()); + m_properties.push_back(std::make_pair(&p, false)); + m_table[name] = &p; +} + +TProperty *TPropertyGroup::getProperty(string name) +{ + PropertyTable::iterator i = m_table.find(name); + if (i == m_table.end()) + return 0; + else + return i->second; +} + +template +void assign(Property *dst, TProperty *src) +{ + Property *s = dynamic_cast(src); + if (!s) + throw TProperty::TypeError(); + dst->setValue(s->getValue()); +} + +class Setter : public TProperty::Visitor +{ + TProperty *m_src; + +public: + Setter(TProperty *src) : m_src(src) {} + + void visit(TDoubleProperty *dst) { assign(dst, m_src); } + void visit(TIntProperty *dst) { assign(dst, m_src); } + void visit(TStringProperty *dst) { assign(dst, m_src); } + void visit(TBoolProperty *dst) { assign(dst, m_src); } + void visit(TEnumProperty *dst) { assign(dst, m_src); } + void visit(TDoublePairProperty *dst) { assign(dst, m_src); } + void visit(TIntPairProperty *dst) { assign(dst, m_src); } + void visit(TStyleIndexProperty *dst) { assign(dst, m_src); } + void visit(TPointerProperty *dst) { assign(dst, m_src); } +}; + +void TPropertyGroup::setProperties(TPropertyGroup *g) +{ + for (PropertyVector::const_iterator i = g->m_properties.begin(); + i != g->m_properties.end(); ++i) { + TProperty *src = i->first; + TProperty *dst = getProperty(src->getName()); + if (dst) { + Setter setter(src); + TProperty::Visitor *visitor = &setter; + dst->accept(*visitor); + } + } +} + +void TPropertyGroup::accept(TProperty::Visitor &v) +{ + for (PropertyVector::const_iterator i = m_properties.begin(); + i != m_properties.end(); ++i) + i->first->accept(v); +} + +class PropertyWriter : public TProperty::Visitor +{ + TOStream &m_os; + +public: + PropertyWriter(TOStream &os) : m_os(os) {} + + void visit(TDoubleProperty *p) + { + std::map attr; + attr["type"] = "double"; + attr["name"] = p->getName(); + attr["min"] = toString(p->getRange().first); + attr["max"] = toString(p->getRange().second); + attr["value"] = toString(p->getValue()); + m_os.openCloseChild("property", attr); + } + void visit(TDoublePairProperty *p) + { + std::map attr; + attr["type"] = "pair"; + attr["name"] = p->getName(); + attr["min"] = toString(p->getRange().first); + attr["max"] = toString(p->getRange().second); + TDoublePairProperty::Value value = p->getValue(); + attr["value"] = toString(value.first) + " " + toString(value.second); + m_os.openCloseChild("property", attr); + } + void visit(TIntPairProperty *p) + { + std::map attr; + attr["type"] = "pair"; + attr["name"] = p->getName(); + attr["min"] = toString(p->getRange().first); + attr["max"] = toString(p->getRange().second); + TIntPairProperty::Value value = p->getValue(); + attr["value"] = toString(value.first) + " " + toString(value.second); + m_os.openCloseChild("property", attr); + } + void visit(TIntProperty *p) + { + std::map attr; + attr["type"] = "int"; + attr["name"] = p->getName(); + attr["min"] = toString(p->getRange().first); + attr["max"] = toString(p->getRange().second); + attr["value"] = toString(p->getValue()); + m_os.openCloseChild("property", attr); + } + void visit(TBoolProperty *p) + { + std::map attr; + attr["type"] = "bool"; + attr["name"] = p->getName(); + attr["value"] = p->getValue() ? "true" : "false"; + m_os.openCloseChild("property", attr); + } + void visit(TStringProperty *p) + { + std::map attr; + attr["type"] = "string"; + attr["name"] = p->getName(); + attr["value"] = toString(p->getValue()); + m_os.openCloseChild("property", attr); + } + + void visit(TStyleIndexProperty *p) + { + std::map attr; + attr["type"] = "string"; + attr["name"] = p->getName(); + attr["value"] = p->getValueAsString(); + m_os.openCloseChild("property", attr); + } + + void visit(TEnumProperty *p) + { + std::map attr; + attr["type"] = "enum"; + attr["name"] = p->getName(); + attr["value"] = toString(p->getValue()); + if (TEnumProperty::isRangeSavingEnabled()) { + m_os.openChild("property", attr); + std::vector range = p->getRange(); + for (int i = 0; i < (int)range.size(); i++) { + attr.clear(); + attr["value"] = toString(range[i]); + m_os.openCloseChild("item", attr); + } + m_os.closeChild(); + } else + m_os.openCloseChild("property", attr); + } + void visit(TPointerProperty *p) + { + std::map attr; + attr["type"] = "pointer"; + attr["name"] = p->getName(); + attr["value"] = p->getValueAsString(); + m_os.openCloseChild("property", attr); + } +}; + +void TPropertyGroup::loadData(TIStream &is) +{ + for (PropertyVector::iterator it = m_properties.begin(); + it != m_properties.end(); ++it) + if (it->second) + delete it->first; + m_properties.clear(); + m_table.clear(); + string tagName; + while (is.matchTag(tagName)) { + if (tagName == "property") { + string name = is.getTagAttribute("name"); + string type = is.getTagAttribute("type"); + string svalue = is.getTagAttribute("value"); + if (name == "") + throw TException("missing property name"); + if (type == "") + throw TException("missing property type"); + if (type != "string" && svalue == "") + throw TException("missing property value"); + if (type == "double") { + double min = toDouble(is.getTagAttribute("min")); + double max = toDouble(is.getTagAttribute("max")); + add(new TDoubleProperty(name, min, max, toDouble(svalue))); + } + if (type == "pair") { + double min = toDouble(is.getTagAttribute("min")); + double max = toDouble(is.getTagAttribute("max")); + TDoublePairProperty::Value v(0, 0); + int i = svalue.find(' '); + if (i != (int)string::npos) { + v.first = toDouble(svalue.substr(0, i)); + v.second = toDouble(svalue.substr(i + 1)); + } + add(new TDoublePairProperty(name, min, max, v.first, v.second)); + } else if (type == "int") { + int min = toInt(is.getTagAttribute("min")); + int max = toInt(is.getTagAttribute("max")); + add(new TIntProperty(name, min, max, toInt(svalue))); + } else if (type == "bool") { + if (svalue != "true" && svalue != "false") + throw TException("bad boolean property value"); + add(new TBoolProperty(name, svalue == "true" ? true : false)); + } else if (type == "string") { + add(new TStringProperty(name, toWideString(svalue))); + } else if (type == "enum") { + TEnumProperty *p = new TEnumProperty(name); + if (is.isBeginEndTag()) + p->addValue(toWideString(svalue)); + else { + while (is.matchTag(tagName)) { + if (tagName == "item") { + string item = is.getTagAttribute("value"); + p->addValue(toWideString(item)); + } else + throw TException("expected range property "); + } + is.closeChild(); + } + p->setValue(toWideString(svalue)); + add(p); + } else + throw TException("unrecognized property type : " + type); + } else + throw TException("expected "); + // is.closeChild(); + } +} + +void TPropertyGroup::saveData(TOStream &os) const +{ + PropertyWriter writer(os); + const_cast(this)->accept(writer); +} + +namespace +{ +bool EnumRangeSavingEnabled = true; +} + +void TEnumProperty::enableRangeSaving(bool on) +{ + EnumRangeSavingEnabled = on; +} + +bool TEnumProperty::isRangeSavingEnabled() +{ + return EnumRangeSavingEnabled; +} diff --git a/toonz/sources/common/traster/traster.cpp b/toonz/sources/common/traster/traster.cpp new file mode 100644 index 0000000..e7f5b48 --- /dev/null +++ b/toonz/sources/common/traster/traster.cpp @@ -0,0 +1,474 @@ + + +#ifdef WIN32 +#ifndef UNICODE +#define UNICODE +#endif +#endif + +#include "tbigmemorymanager.h" +#include "traster.h" +#include "trastercm.h" +//#include "tspecialstyleid.h" +#include "tpixel.h" +#include "tpixelgr.h" +#include "timagecache.h" + +DEFINE_CLASS_CODE(TRaster, 1) + +//------------------------------------------------------------ + +TRaster::TRaster(int lx, int ly, int pixelSize) + : TSmartObject(m_classCode), m_pixelSize(pixelSize), m_lx(lx), m_ly(ly), m_wrap(lx), m_parent(0), m_bufferOwner(true), m_buffer(0), m_lockCount(0) +#ifdef _DEBUG + , + m_cashed(false) +#endif + +{ + //try + { + assert(pixelSize > 0); + assert(lx > 0 && ly > 0); + TBigMemoryManager::instance()->putRaster(this); + + //m_buffer = new UCHAR[lx*ly*pixelSize]; + + if (!m_buffer) { +#ifdef WIN32 + static bool firstTime = true; + if (firstTime) { + firstTime = false; + unsigned long size = pixelSize * lx * ly; + TImageCache::instance()->outputMap(size, "C:\\runout"); + (*TBigMemoryManager::instance()->m_runOutCallback)(size); + } +#endif + return; + } + + // TBigMemoryManager::instance()->checkConsistency(); + + //m_totalMemory += ((lx*ly*pixelSize)>>10); + } + /* catch(...) + { + TImageCache::instance()->putAllOnDisk(); + m_buffer = BigMemoryManager.getMemoryChunk(lx*ly*pixelSize, this); + //m_buffer = new UCHAR[lx*ly*pixelSize]; + m_totalMemory += ((lx*ly*pixelSize)>>10); + #ifdef WIN32 + MessageBox( NULL, "Run out of contiguos phisical memory: please save all and restart toonz!", "Warning", MB_OK); + #endif + }*/ +} + +//------------------------------------------------------------ + +TRaster::TRaster(int lx, int ly, int pixelSize, + int wrap, UCHAR *buffer, TRaster *parent, bool bufferOwner) + : TSmartObject(m_classCode), m_pixelSize(pixelSize), m_lx(lx), m_ly(ly), m_wrap(wrap), m_buffer(buffer), m_bufferOwner(bufferOwner), m_lockCount(0) +#ifdef _DEBUG + , + m_cashed(false) +#endif + +{ + if (parent) { + assert(bufferOwner == false); + while (parent->m_parent) + parent = parent->m_parent; + parent->addRef(); + } +#ifdef _DEBUG + else if (bufferOwner) + TBigMemoryManager::instance()->m_totRasterMemInKb += ((m_lx * m_ly * m_pixelSize) >> 10); +#endif + + m_parent = parent; + + assert(pixelSize > 0); + assert(lx > 0 && ly > 0); + assert(wrap >= lx); + assert(m_buffer); + //if (parent) + TBigMemoryManager::instance()->putRaster(this); + + // TBigMemoryManager::instance()->checkConsistency(); +} + +//------------------------------------------------------------ + +//TAtomicVar TRaster::m_totalMemory; + +//------------------------------------------------------------ + +//unsigned long TRaster::getTotalMemoryInKB(){ return m_totalMemory;} + +//------------------------------------------------------------ + +TRaster::~TRaster() +{ + bool parent = false; +#ifdef _DEBUG +//TBigMemoryManager::instance()->checkConsistency(); +#endif + //bool ret = + TBigMemoryManager::instance()->releaseRaster(this); +#ifdef _DEBUG +//TBigMemoryManager::instance()->checkConsistency(); +#endif + if (m_parent) { + assert(!m_bufferOwner); + m_parent->release(); + m_parent = 0; + parent = true; + } + + //if(m_buffer && m_bufferOwner) + // { + //delete [] m_buffer; + //m_totalMemory += -((m_lx*m_ly*m_pixelSize)>>10); + //assert(m_totalMemory>=0); + // } + // UCHAR* aux = m_buffer; + m_buffer = 0; + +#ifdef _DEBUG +//TBigMemoryManager::instance()->checkConsistency(); +#endif +} + +//------------------------------------------------------------ +void TRaster::beginRemapping() +{ + m_mutex.lock(); +} + +void TRaster::endRemapping() +{ + m_mutex.unlock(); +} + +/* + void TRaster::lock() + { + if (m_parent) m_parent->lock(); + else ++m_lockCount; + //TBigMemoryManager::instance()->lock(m_parent?(m_parent->m_buffer):m_buffer); + } + + void TRaster::unlock() + { + if (m_parent) m_parent->unlock(); + else + { + assert(m_lockCount>0); + --m_lockCount; + } + + //TBigMemoryManager::instance()->unlock(m_parent?(m_parent->m_buffer):m_buffer); + } +*/ + +/* +template + TRasterT::TRasterT(int lx, int ly) : TRaster(lx,ly,sizeof(T)) {} + + // utilizzo di un raster preesistente + template + TRasterT::TRasterT(int lx, int ly, int wrap, T *buffer, TRasterT *parent) + : TRaster(lx,ly,sizeof(T), wrap + , reinterpret_cast(buffer), parent) {} +*/ +//------------------------------------------------------------ + +void TRaster::fillRawData(const UCHAR *color) +{ + if (m_lx == 0 || m_ly == 0) + return; + + // N.B. uso la convenzione stl per end() + + const int wrapSize = m_wrap * m_pixelSize; + const int rowSize = m_lx * m_pixelSize; + UCHAR *buf1 = m_parent ? m_parent->m_buffer : m_buffer; + lock(); + unsigned char *firstPixel = getRawData(); + const unsigned char *lastPixel = firstPixel + + wrapSize * (m_ly - 1) + m_pixelSize * (m_lx - 1); + + // riempio la prima riga + unsigned char *pixel = firstPixel; + const unsigned char *endpixel = firstPixel + rowSize; + while (pixel < endpixel) { + assert(firstPixel <= pixel && pixel <= lastPixel); + ::memcpy(pixel, color, m_pixelSize); + pixel += m_pixelSize; + } + + // riempio le altre + pixel += wrapSize - rowSize; + const unsigned char *endrow = pixel + wrapSize * (m_ly - 1); + while (pixel < endrow) { + assert(firstPixel <= pixel && pixel + rowSize - m_pixelSize <= lastPixel); + ::memcpy(pixel, firstPixel, rowSize); + pixel += wrapSize; + } + UCHAR *buf2 = m_parent ? m_parent->m_buffer : m_buffer; + unlock(); +} + +//------------------------------------------------------------ + +void TRaster::fillRawDataOutside(const TRect &rect, const unsigned char *pixel) +{ + if (m_lx == 0 || m_ly == 0) + return; + TRect r = rect * getBounds(); + if (r.isEmpty()) + return; + + if (r.y0 > 0) // fascia "inferiore" + { + TRect bounds(0, 0, m_lx - 1, r.y0 - 1); + extract(bounds)->fillRawData(pixel); + } + + if (rect.y1 < m_ly - 1) // fascia "superiore" + { + TRect bounds(0, r.y1 + 1, m_lx - 1, m_ly - 1); + extract(bounds)->fillRawData(pixel); + } + + if (rect.x0 > 0) // zona "a sinistra" + { + TRect bounds(0, r.y0, r.x0 - 1, r.y1); + extract(bounds)->fillRawData(pixel); + } + + if (rect.x1 < m_lx - 1) // zona "a destra" + { + TRect bounds(r.x1 + 1, r.y0, m_lx - 1, r.y1); + extract(bounds)->fillRawData(pixel); + } +} + +//------------------------------------------------------------ + +void TRaster::copy(const TRasterP &src0, const TPoint &offset) +{ + assert(m_pixelSize == src0->getPixelSize()); + TRect rect = getBounds() * (src0->getBounds() + offset); + if (rect.isEmpty()) + return; + TRasterP dst = extract(rect); + TRect r(rect); + r -= offset; + //TRasterP src = src0->extract(rect - offset); + TRasterP src = src0->extract(r); + assert(dst->getSize() == src->getSize()); + dst->lock(); + src0->lock(); + if (dst->getLx() == dst->getWrap() && src->getLx() == src->getWrap()) { + int size = rect.getLx() * rect.getLy() * m_pixelSize; + ::memcpy(dst->getRawData(), src->getRawData(), size); + } else { + int rowSize = dst->getLx() * m_pixelSize; + int srcWrapSize = src->getWrap() * m_pixelSize; + int dstWrapSize = dst->getWrap() * m_pixelSize; + const UCHAR *srcRow = src->getRawData(); + UCHAR *dstRow = dst->getRawData(); + UCHAR *maxDstRow = dstRow + dstWrapSize * dst->getLy(); + while (dstRow < maxDstRow) { + ::memcpy(dstRow, srcRow, rowSize); + dstRow += dstWrapSize; + srcRow += srcWrapSize; + } + } + dst->unlock(); + src0->unlock(); +} + +//------------------------------------------------------------ + +void TRaster::yMirror() +{ + const int rowSize = m_lx * m_pixelSize; + const int wrapSize = m_wrap * m_pixelSize; + UCHAR *auxBuf = new UCHAR[rowSize]; + lock(); + UCHAR *buff1 = getRawData(); + UCHAR *buff2 = getRawData(0, (m_ly - 1)); + while (buff1 < buff2) { + ::memcpy(auxBuf, buff1, rowSize); + ::memcpy(buff1, buff2, rowSize); + ::memcpy(buff2, auxBuf, rowSize); + buff1 += wrapSize; + buff2 -= wrapSize; + } + unlock(); + delete[] auxBuf; +} + +//------------------------------------------------------------ + +void TRaster::xMirror() +{ + const int wrapSize = m_wrap * m_pixelSize; + const int lastPixelOffset = (m_lx - 1) * m_pixelSize; + UCHAR *auxBuf = new UCHAR[m_pixelSize]; + lock(); + UCHAR *row = getRawData(); + for (int i = 0; i < m_ly; i++) { + UCHAR *a = row, *b = row + lastPixelOffset; + while (a < b) { + ::memcpy(auxBuf, a, m_pixelSize); + ::memcpy(a, b, m_pixelSize); + ::memcpy(b, auxBuf, m_pixelSize); + a += m_pixelSize; + b -= m_pixelSize; + } + row += wrapSize; + } + unlock(); + delete[] auxBuf; +} + +//------------------------------------------------------------ + +void TRaster::rotate180() +{ + //const int rowSize = m_lx * m_pixelSize; + const int wrapSize = m_wrap * m_pixelSize; + UCHAR *auxBuf = new UCHAR[m_pixelSize]; + lock(); + UCHAR *buff1 = getRawData(); + UCHAR *buff2 = buff1 + wrapSize * (m_ly - 1) + m_pixelSize * (m_lx - 1); + if (m_wrap == m_lx) { + while (buff1 < buff2) { + ::memcpy(auxBuf, buff1, m_pixelSize); + ::memcpy(buff1, buff2, m_pixelSize); + ::memcpy(buff2, auxBuf, m_pixelSize); + buff1 += m_pixelSize; + buff2 -= m_pixelSize; + } + } else { + for (int y = 0; y < m_ly / 2; y++) { + UCHAR *a = buff1, *b = buff2; + for (int x = 0; x < m_lx; x++) { + ::memcpy(auxBuf, a, m_pixelSize); + ::memcpy(a, b, m_pixelSize); + ::memcpy(b, auxBuf, m_pixelSize); + a += m_pixelSize; + b -= m_pixelSize; + } + buff1 += wrapSize; + buff2 -= wrapSize; + } + } + unlock(); + delete[] auxBuf; +} + +//------------------------------------------------------------ + +void TRaster::rotate90() +{ + /* + UCHAR *auxBuf= new UCHAR[m_pixelSize]; + + + for(int y=m_ly;y>0;y--) + { + UCHAR *a = getRawData() + wrapSize * (y-1) + m_pixelSize * (m_lx-1); + for (int x=m_lx-1;x>=0;x--) + { + UCHAR *b = a - (m_ly-1)*m_pixelSize *(m_lx-x); + ::memcpy(auxBuf, a, m_pixelSize); + ::memcpy(a, b, m_pixelSize); + ::memcpy(b, auxBuf, m_pixelSize); + a-=m_pixelSize; + } + + } + */ +} + +//------------------------------------------------------------ + +void TRaster::clear() +{ + TRasterCM32 *ras = dynamic_cast(this); + if (ras) { + // ras->fill(TPixelCM32(0,BackgroundStyle,TPixelCM32::getMaxTone())); + ras->fill(TPixelCM32()); + } else { + const int rowSize = getRowSize(); + lock(); + if (m_wrap == m_lx) { + int bufferSize = rowSize * m_ly; + memset(getRawData(), 0, bufferSize); + } else + for (int y = m_ly - 1; y >= 0; y--) { + UCHAR *buffer = getRawData(0, y); + memset(buffer, 0, rowSize); + } + unlock(); + } +} + +//------------------------------------------------------------ + +void TRaster::remap(UCHAR *newLocation) +{ + if (m_parent) { + assert(m_parent->m_buffer > newLocation); + + assert(m_parent->m_parent == 0); + + int offset = (int)(m_buffer - m_parent->m_buffer); + assert(offset >= 0); + + //m_parent->remap(newLocation); + m_buffer = newLocation + offset; + } else { + assert(m_buffer > newLocation); + m_buffer = newLocation; + } +} + +//------------------------------------------------------------ + +void TRaster::clearOutside(const TRect &rect) +{ + if (m_lx == 0 || m_ly == 0) + return; + TRect r = rect * getBounds(); + if (r.isEmpty()) + return; + + if (r.y0 > 0) // fascia "inferiore" + { + TRect bounds(0, 0, m_lx - 1, r.y0 - 1); + extract(bounds)->clear(); + } + + if (rect.y1 < m_ly - 1) // fascia "superiore" + { + TRect bounds(0, r.y1 + 1, m_lx - 1, m_ly - 1); + extract(bounds)->clear(); + } + + if (rect.x0 > 0) // zona "a sinistra" + { + TRect bounds(0, r.y0, r.x0 - 1, r.y1); + extract(bounds)->clear(); + } + + if (rect.x1 < m_lx - 1) // zona "a destra" + { + TRect bounds(r.x1 + 1, r.y0, m_lx - 1, r.y1); + extract(bounds)->clear(); + } +} diff --git a/toonz/sources/common/trasterimage/tcachedlevel.cpp b/toonz/sources/common/trasterimage/tcachedlevel.cpp new file mode 100644 index 0000000..8edcccd --- /dev/null +++ b/toonz/sources/common/trasterimage/tcachedlevel.cpp @@ -0,0 +1,1673 @@ + + +#include "tsystem.h" +#include "tcachedlevel.h" + +#include "tcodec.h" + +#include "texception.h" +//#include "tcachedlevel.h" +//#include "tcodec.h" + +#include "tconvert.h" + +#ifdef LINUX +#include "texception.h" +//#include "tsystem.h" +#include +#include +#include +#include +#include +#endif + +#ifdef MACOSX +#include "sys/mman.h" +#include "sys/errno.h" +#endif +//------------------------------------------------------------------------------ + +#define DWORDLONG_LO_DWORD(dwl64) ((DWORD)(dwl64)) +#define DWORDLONG_HI_DWORD(dwl64) ((DWORD)(dwl64 >> 32)) + +//============================================================================== +//============================================================================== +//============================================================================== +// TDiskCachePersist +//------------------------------------------------------------------------------ + +class ImpPD +{ +public: + ImpPD(const TFilePath &fn) + : m_fname(fn), m_chunkSize(0), m_currentFileSize(0), m_fileMapAddress(0), m_mapOffset(0) + { + TFileStatus fileStatus(fn); + if (fileStatus.doesExist()) + m_currentFileSize = fileStatus.getSize(); + else + m_currentFileSize = ImpPD::m_defaultFileSize; + }; + virtual ~ImpPD() {} + virtual void openFile(const TFilePath &, TINT64 fileSize) = 0; + virtual void setCurrentView(int pos, int &newLowPos, int &newHiPos) = 0; + TFilePath m_fname; + + TINT64 m_chunkSize; + TINT64 m_currentFileSize; + void *m_fileMapAddress; + TINT64 m_mapOffset; + + // quantita' espresse in byte + static TINT64 m_defaultFileSize; + TUINT32 m_viewSize; + TINT64 m_reallocSize; +}; + +TINT64 ImpPD::m_defaultFileSize(100 * 1024 * 1024); + +//------------------------------------------------------------------------------ + +class TDiskCachePersist::Imp +{ +public: + Imp(const TFilePath &fp); + ~Imp(); + + bool put(int frame, UCHAR *data, TUINT32 dataSize); + UCHAR *get(int pos, TUINT32 *size); + void openFile(const TFilePath &fp, TINT64 fileSize); + void setCurrentView(int frame) + { + if (!m_force && ((m_lowFrame <= frame) && (frame < m_hiFrame))) + return; //la vista corrente gia' copre il frame + m_force = false; + m_impPD->setCurrentView(frame, m_lowFrame, m_hiFrame); + } + + ImpPD *m_impPD; + + int m_lowFrame, m_hiFrame; + TThread::Mutex m_mutex; + + bool m_force; +}; + +#ifdef WIN32 +class ImpPDW : public ImpPD +{ +private: + string getLastErrorMessage() + { + LPVOID lpMsgBuf; + + DWORD err = GetLastError(); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR)&lpMsgBuf, + 0, + NULL); + + string msg((LPCTSTR)lpMsgBuf); + + // Free the buffer. + LocalFree(lpMsgBuf); + return msg; + } + +public: + ImpPDW(const TFilePath &fp); + ~ImpPDW(); + void openFile(const TFilePath &fname, TINT64 fileSize); + void setCurrentView(int pos, int &newLowPos, int &newHiPos); + +private: + HANDLE m_hFile; + HANDLE m_hMap; + + SYSTEM_INFO m_systemInfo; +}; + +#else + +class ImpPDX : public ImpPD +{ +private: + class TCachedLevelException : public TException + { + static string msgFromErrorCode(int errorCode) + { + switch (errorCode) { + case EBADF: + return " fd is not a valid file descriptor (and MAP_ANONY­MOUS was not set)."; + break; + /* + case EACCES_MAP_PRIVATE: + return "Map private was requested, but fd is not open for reading. Or MAP_SHARED was requested and PROT_WRITE is set, but fd is not open in read/write O_RDWR) mode."; + break; +*/ + case EINVAL: + return "We don't like start or length or offset. (E.g., they are too large, or not aligned on a PAGESIZE boundary.)"; + break; + + case ETXTBSY: + return "MAP_DENYWRITE was set but the object specified by fd is open for writing."; + break; + + case EAGAIN: + return "The file has been locked, or too much memory has been locked."; + break; + + case ENOMEM: + return "No memory is available."; + break; + + default: + char *sysErr = strerror(errorCode); + ostrstream os; + os << errorCode << '\0'; + os.freeze(false); + return string(sysErr) + "(" + os.str() + ")"; + break; + } + return ""; + } + + public: + TCachedLevelException(int errorCode) + : TException(msgFromErrorCode(errorCode)) + { + } + ~TCachedLevelException() {} + }; + +public: + ImpPDX(const TFilePath &fp); + ~ImpPDX(); + void openFile(const TFilePath &fname, TINT64 fileSize); + void setCurrentView(int pos, int &newLowPos, int &newHiPos); + +private: + int m_fd; + size_t m_pageSize; +}; + +#endif + +// HIGH +//------------------------------------------------------------------------------ + +TDiskCachePersist::TDiskCachePersist(TRasterCodec *codec, const TFilePath &fp) + : TCachePersist(codec), m_imp(new Imp(fp)) +{ +} + +//------------------------------------------------------------------------------ + +TDiskCachePersist::~TDiskCachePersist() +{ + delete m_imp; +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist::setFrameSize(int lx, int ly, int bpp) +{ + m_imp->m_impPD->m_chunkSize = lx * ly * (bpp >> 3) + m_codec->getHeaderSize(); + m_imp->m_force = true; +} + +//------------------------------------------------------------------------------ + +TRasterP TDiskCachePersist::doGetRaster(int frame) +{ + TRasterP rasP; + TUINT32 size; + UCHAR *src = m_imp->get(frame, &size); + m_codec->decompress(src, size, rasP); + delete[] src; + return rasP; +} + +//------------------------------------------------------------------------------ + +bool TDiskCachePersist::doGetRaster(int frame, TRaster32P &ras) const +{ + assert(false); + return false; +} + +//------------------------------------------------------------------------------ + +bool TDiskCachePersist::doPutRaster(int frame, const TRasterP &ras) +{ + UCHAR *outData = 0; + TINT32 outDataSize = 0; + m_codec->compress(ras, 1, &outData, outDataSize); + bool cached = m_imp->put(frame, outData, outDataSize); + delete[] outData; + return cached; +} + +//------------------------------------------------------------------------------ + +UCHAR *TDiskCachePersist::getRawData(int frame, TINT32 &size, int &lx, int &ly) +{ + TUINT32 inDataSize; + UCHAR *src = m_imp->get(frame, &inDataSize); + return m_codec->removeHeader(src, inDataSize, size, lx, ly); +} + +//------------------------------------------------------------------------------ +// MEDIUM +TDiskCachePersist::Imp::Imp(const TFilePath &fp) + : m_impPD(0) +{ +#ifdef WIN32 + m_impPD = new ImpPDW(fp); +#else + m_impPD = new ImpPDX(fp); +#endif + m_impPD->m_currentFileSize = TFileStatus(fp).doesExist() ? TFileStatus(fp).getSize() : 0; // per gli arrotondamenti... +} + +//------------------------------------------------------------------------------ + +TDiskCachePersist::Imp::~Imp() +{ + delete m_impPD; +} +//------------------------------------------------------------------------------ +// LOW + +#ifdef WIN32 +ImpPDW::ImpPDW(const TFilePath &fp) + : ImpPD(fp), m_hFile(0), m_hMap(0) +{ + GetSystemInfo(&m_systemInfo); + + m_viewSize = 100 * 1024 * 1024; + m_reallocSize = 250 * 1024 * 1024; + + TINT64 allocUnitCount = m_reallocSize / m_systemInfo.dwAllocationGranularity; + + // rendo m_reallocSize multiplo di m_systemInfo.dwAllocationGranularity + if ((m_reallocSize % m_systemInfo.dwAllocationGranularity) != 0) + ++allocUnitCount; + + m_reallocSize = allocUnitCount * m_systemInfo.dwAllocationGranularity; + + TINT64 fileSize = m_defaultFileSize; + + TFileStatus fileStatus(fp); + if (fileStatus.doesExist()) + fileSize = fileStatus.getSize(); + + try { + openFile(fp, fileSize); + } catch (TException &e) { + m_currentFileSize = 0; + throw e; + } + + m_currentFileSize = fileSize; +} + +//------------------------------------------------------------------------------ + +ImpPDW::~ImpPDW() +{ + if (m_fileMapAddress) + UnmapViewOfFile(m_fileMapAddress); + CloseHandle(m_hMap); + CloseHandle(m_hFile); +} + +//------------------------------------------------------------------------------ + +void ImpPDW::openFile(const TFilePath &fname, TINT64 fileSize) +{ + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + DWORD dwShareMode = 0; // dwShareMode == 0 --> accesso esclusivo + + // lpSecurityAttributes == NULL --> l'handle non puo' essere + // ereditato da processi figli + LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL; + + DWORD dwCreationDisposition = OPEN_ALWAYS; //CREATE_ALWAYS; + DWORD dwFlagsAndAttributes = FILE_FLAG_SEQUENTIAL_SCAN; //FILE_ATTRIBUTE_NORMAL;// + + HANDLE hTemplateFile = NULL; + + m_hFile = CreateFileW( + fname.getWideString().c_str(), // file name + dwDesiredAccess, // access mode + dwShareMode, // share mode + NULL, // SD + dwCreationDisposition, // how to create + dwFlagsAndAttributes, // file attributes + hTemplateFile // handle to template file + ); + + if (m_hFile == INVALID_HANDLE_VALUE) { + string errMsg = getLastErrorMessage(); + throw TException(wstring(L"Unable to open cache file: ") + fname.getWideString() + L"\n" + toWideString(errMsg)); + } + + DWORD flProtect = PAGE_READWRITE; + DWORD dwMaximumSizeHigh = DWORDLONG_HI_DWORD(fileSize); + DWORD dwMaximumSizeLow = DWORDLONG_LO_DWORD(fileSize); + LPCTSTR lpName = NULL; // l'oggetto non ha nome + + m_hMap = CreateFileMapping( + m_hFile, // handle to file + NULL, // security + flProtect, // protection + dwMaximumSizeHigh, // high-order DWORD of size + dwMaximumSizeLow, // low-order DWORD of size + lpName // object name + ); + + if (m_hMap == NULL) { + string errMsg = getLastErrorMessage(); + CloseHandle(m_hFile); + m_hFile = 0; + throw TException("Unable to create file mapping. " + errMsg); + } +} + +//------------------------------------------------------------------------------ + +void ImpPDW::setCurrentView(int frame, int &newLowFrame, int &newHiFrame) +{ + if (m_fileMapAddress) + UnmapViewOfFile(m_fileMapAddress); + + newLowFrame = frame; + + newHiFrame = newLowFrame + TINT32(m_viewSize / m_chunkSize); + + DWORD allocGranularity = m_systemInfo.dwAllocationGranularity; + TINT64 viewOffset = (TINT64(newLowFrame * m_chunkSize) / allocGranularity) * allocGranularity; + m_mapOffset = newLowFrame * m_chunkSize - viewOffset; + + TINT64 fileSize = newHiFrame * m_chunkSize; + + if ((fileSize > m_currentFileSize) || !m_hMap) // devo riallocare! + { + CloseHandle(m_hMap); + m_hMap = 0; + CloseHandle(m_hFile); + m_hFile = 0; + + TINT64 allocUnitCount = fileSize / m_reallocSize; + // rendo fileSize multiplo di m_reallocSize + if ((fileSize % m_reallocSize) != 0) + ++allocUnitCount; + + fileSize = allocUnitCount * m_reallocSize; + + openFile(m_fname, fileSize); + m_currentFileSize = fileSize; + } + + DWORD dwDesiredAccess = FILE_MAP_WRITE; + m_fileMapAddress = MapViewOfFile(m_hMap, // handle to file-mapping object + dwDesiredAccess, // access mode: Write permission + DWORDLONG_HI_DWORD(viewOffset), // high-order DWORD of offset: Max. object size. + DWORDLONG_LO_DWORD(viewOffset), // low-order DWORD of offset: Size of hFile. + m_viewSize); // number of bytes to map + + if (m_fileMapAddress == NULL) { + string errMsg = getLastErrorMessage(); + CloseHandle(m_hMap); + m_hMap = 0; + CloseHandle(m_hFile); + m_hFile = 0; + + throw TException("Unable to memory map cache file. " + errMsg); + } +} +#else + +ImpPDX::ImpPDX(const TFilePath &fp) + : ImpPD(fp), m_fd(-1) +{ + //std::cout << "cache file " << toString(m_fname.getFullPath()) << std::endl; + m_pageSize = getpagesize(); + openFile(m_fname, 0); + assert(m_fd >= 0); +} + +//------------------------------------------------------------------------------ + +ImpPDX::~ImpPDX() +{ + if (m_fileMapAddress) + munmap(m_fileMapAddress, m_viewSize); + close(m_fd); + m_fd = 0; +} + +//------------------------------------------------------------------------------ + +void ImpPDX::openFile(const TFilePath &fname, TINT64 fileSize) +{ + assert(0); + /* +string fn(toString(fname.getWideString())); +std::cout << "open " << fn << std::endl; +m_fd = open(fn.c_str(), O_RDWR|O_CREAT, 00666); +assert(m_fd >=0); +*/ +} + +void ImpPDX::setCurrentView(int pos, int &newLowPos, int &newHiPos) +{ + newLowPos = pos; + newHiPos = newLowPos + (m_viewSize / m_chunkSize); + + assert(m_fd >= 0); + if (m_fileMapAddress) //previous view... + if (munmap(m_fileMapAddress, m_viewSize) != 0) + throw TCachedLevelException(errno); + void *start = 0; + int flags = MAP_SHARED; + size_t viewOffset = ((newLowPos * m_chunkSize) / m_pageSize) * m_pageSize; + m_mapOffset = newLowPos * m_chunkSize - viewOffset; + + assert(!"controllare le dimensioni"); + unsigned long lastByte = (unsigned long)(((newHiPos * m_chunkSize) / (double)m_pageSize + 0.5) * m_pageSize); + + if (lastByte > m_currentFileSize) // devo riallocare! + { + unsigned long bu = (unsigned long)((lastByte / (double)m_reallocSize + 0.5) * m_reallocSize); + bu = (unsigned long)((bu / (double)m_pageSize + 0.5) * m_pageSize); + //m_maxFileSize = tmax(m_maxFileSize + m_reallocFileSize, lastByte); + m_currentFileSize += bu; + + std::cout << "new cache size " << m_currentFileSize << std::endl; + if (lseek(m_fd, m_currentFileSize, SEEK_SET) == -1) + throw TCachedLevelException(errno); + if (write(m_fd, "", 1) == -1) + throw TCachedLevelException(errno); + if (ftruncate(m_fd, m_currentFileSize) == -1) + throw TCachedLevelException(errno); + } + + m_fileMapAddress = mmap(start, m_viewSize, PROT_READ | PROT_WRITE, flags, m_fd, viewOffset); + if (m_fileMapAddress == (void *)-1) + throw TCachedLevelException(errno); +} + +#endif + +//------------------------------------------------------------------------------ +#ifndef WIN32 +#define ULONGLONG unsigned long long +#endif +bool TDiskCachePersist::Imp::put(int frame, UCHAR *data, TUINT32 dataSize) +{ + if (dataSize != m_impPD->m_chunkSize) + return false; + + TThread::ScopedLock sl(m_mutex); + + setCurrentView(frame); + ULONGLONG offset = (frame - m_lowFrame) * m_impPD->m_chunkSize; + UCHAR *dst = (UCHAR *)m_impPD->m_fileMapAddress + offset; + memcpy(dst + m_impPD->m_mapOffset, data, dataSize); + return true; +} + +//------------------------------------------------------------------------------ + +UCHAR *TDiskCachePersist::Imp::get(int pos, TUINT32 *size) +{ + UCHAR *ret = new UCHAR[TINT32(m_impPD->m_chunkSize)]; + + TThread::ScopedLock sl(m_mutex); + setCurrentView(pos); + ULONGLONG offset = (pos - m_lowFrame) * m_impPD->m_chunkSize; + UCHAR *src = (UCHAR *)m_impPD->m_fileMapAddress + offset + m_impPD->m_mapOffset; + memcpy(ret, src, TINT32(m_impPD->m_chunkSize)); + *size = TUINT32(m_impPD->m_chunkSize); + return ret; +} +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// TRasterCache +//------------------------------------------------------------------------------ + +class TRasterCache::Data +{ +public: + Data(TCachePersist *cp) + : m_cp(cp), m_size(0, 0), m_prefetchEnabled(false), m_prefetchedFrame(-1), m_frameToPrefetch(-1), m_preLoader(1, true) + { + } + ~Data() {} + + class FrameData + { + public: + bool m_valid; + // int m_size; // dimensione in byte del raster codificato + ~FrameData() {} + }; + + bool isFrameCached(int frame) const; + + TDimension m_size; // dimensioni dei raster in cache + int m_bpp; + + typedef map Status; + Status m_status; + TCachePersist *m_cp; + TThread::Mutex m_accessMutex; + + TThread::Executor m_preLoader; + bool m_prefetchEnabled; + int m_prefetchedFrame; + int m_frameToPrefetch; + TRasterP m_prefetchedRas; +}; + +bool TRasterCache::Data::isFrameCached(int frame) const +{ + // volutamente senza ScopedLock + Data::Status::const_iterator it = m_status.find(frame); + if (it == m_status.end()) + return false; + Data::FrameData fd = it->second; + return fd.m_valid; +} + +//============================================================================== + +namespace +{ + +class Load : public TThread::Runnable +{ +public: + Load(int frameToPrefetch, TCachePersist *cp, int &prefetchedFrame, TRasterP &prefetchedRas) + : m_frame(frameToPrefetch), m_cp(cp), m_prefetchedFrame(prefetchedFrame), m_prefetchedRas(prefetchedRas) {} + + void run(); + +private: + int m_frame; + TCachePersist *m_cp; + int &m_prefetchedFrame; + TRasterP &m_prefetchedRas; +}; + +void Load::run() +{ + m_prefetchedRas = m_cp->doGetRaster(m_frame); + m_prefetchedFrame = m_frame; +} +}; + +//============================================================================== + +TRasterCache::TRasterCache(TCachePersist *cp) + : m_data(new Data(cp)) +{ +} + +//------------------------------------------------------------------------------ + +TRasterCache::~TRasterCache() +{ + delete m_data; +} + +//------------------------------------------------------------------------------ +void TRasterCache::setMode(const TDimension &size, int bpp) +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + + m_data->m_size = size; // dimensioni dei raster in cache + m_data->m_bpp = bpp; + + m_data->m_cp->setFrameSize(size.lx, size.ly, bpp); + invalidate(); +} + +//------------------------------------------------------------------------------ + +void TRasterCache::getMode(TDimension &size, int &bpp) const +{ + size = m_data->m_size; // dimensioni dei raster in cache + bpp = m_data->m_bpp; +} + +//------------------------------------------------------------------------------ + +TRasterP TRasterCache::getRaster(int frame) const +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + + if (m_data->m_prefetchEnabled) { + /* + if (frame == m_data->m_frameToPrefetch) + m_data->m_preLoader.wait(); + else + */ + { + m_data->m_preLoader.clear(); + //m_data->m_preLoader.cancel(); + } + + TRasterP ras; + if (frame == m_data->m_prefetchedFrame) + ras = m_data->m_prefetchedRas; + else + ras = m_data->m_cp->doGetRaster(frame); + + if (isFrameCached(frame + 1)) { + // il frame successivo a quello richiesto e' nella cache + // -> avvia il prefetch di tale raster + m_data->m_frameToPrefetch = frame + 1; + m_data->m_preLoader.addTask( + new Load(m_data->m_frameToPrefetch, m_data->m_cp, + m_data->m_prefetchedFrame, m_data->m_prefetchedRas)); + } + + return ras; + } else { + return m_data->m_cp->doGetRaster(frame); + } +} + +//------------------------------------------------------------------------------ + +bool TRasterCache::getRaster(int frame, TRaster32P &ras) const +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + if (m_data->isFrameCached(frame)) { + bool rc = m_data->m_cp->doGetRaster(frame, ras); + assert(rc); + return true; + } else + return false; +} + +//------------------------------------------------------------------------------ + +void TRasterCache::putRaster(int frame, const TRasterP &ras) +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + Data::Status::iterator it = m_data->m_status.find(frame); + bool cached = false; + try { + cached = m_data->m_cp->doPutRaster(frame, ras); + } catch (TException &e) { + if (it != m_data->m_status.end()) { + Data::FrameData fd; + fd.m_valid = false; + m_data->m_status[frame] = fd; + } + throw e; + } + if (cached) { + Data::FrameData fd; + fd.m_valid = true; + m_data->m_status[frame] = fd; + } +} + +//------------------------------------------------------------------------------ + +UCHAR *TRasterCache::getRawData(int frame, TINT32 &size, int &lx, int &ly) const +{ + return m_data->m_cp->getRawData(frame, size, lx, ly); +} + +//------------------------------------------------------------------------------ + +bool TRasterCache::isFrameCached(int frame) const +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + return m_data->isFrameCached(frame); +} + +//------------------------------------------------------------------------------ + +void TRasterCache::invalidate() +{ + TThread::ScopedLock sl(m_data->m_accessMutex); + m_data->m_status.clear(); + m_data->m_cp->onInvalidate(); +} + +//------------------------------------------------------------------------------ + +void TRasterCache::invalidate(int startFrame, int endFrame) +{ + assert(startFrame <= endFrame); + TThread::ScopedLock sl(m_data->m_accessMutex); + + Data::Status::iterator low = m_data->m_status.lower_bound(startFrame); + Data::Status::iterator hi = m_data->m_status.upper_bound(endFrame); + +#ifdef _DEBUG + int count = m_data->m_status.size(); + + if (low != m_data->m_status.end() && hi != m_data->m_status.end()) { + int ll = low->first; + int hh = hi->first; + assert(ll <= hh); + } +#endif + + if (low != m_data->m_status.end()) { + m_data->m_status.erase(low, hi); + m_data->m_cp->onInvalidate(startFrame, endFrame); + } +} + +//------------------------------------------------------------------------------ + +void TRasterCache::enablePrefetch(bool newState) +{ + m_data->m_prefetchEnabled = newState; +} + +//------------------------------------------------------------------------------ + +bool TRasterCache::isPrefetchEnabled() const +{ + return m_data->m_prefetchEnabled; +} + +//------------------------------------------------------------------------------ + +TUINT64 TRasterCache::getUsedSpace() +{ + return m_data->m_cp->getUsedSpace(); +} + +//============================================================================== +//============================================================================== +//============================================================================== +// TRamCachePersist +//------------------------------------------------------------------------------ +class TRamCachePersist::Imp +{ + friend class TRamCachePersist; + +public: + Imp() + : m_cacheSize(0), m_chunks() + { + } + ~Imp() + { + for (CompressedChunks::iterator it = m_chunks.begin(); it != m_chunks.end(); ++it) { + CompressedChunk *cc = it->second; + m_cacheSize -= cc->m_size; + delete cc; + } + assert(m_cacheSize == 0); // se m_cacheSize > 0 mi sono perso qualche chunk + // se m_cacheSize < 0 ho liberato 2 volte qualche chunk + m_chunks.clear(); + } + + class CompressedChunk + { + public: + CompressedChunk(UCHAR *buffer, int size) : m_buffer(buffer), m_size(size) {} + ~CompressedChunk() + { + delete[] m_buffer; + } + UCHAR *m_buffer; + int m_size; + }; + + typedef map CompressedChunks; + CompressedChunks m_chunks; + TUINT64 m_cacheSize; +}; + +TRamCachePersist::TRamCachePersist(TRasterCodec *codec) + : TCachePersist(codec), m_imp(new Imp) +{ +} + +//------------------------------------------------------------------------------ + +TRamCachePersist::~TRamCachePersist() +{ + delete m_imp; +} + +//------------------------------------------------------------------------------ + +TRasterP TRamCachePersist::doGetRaster(int frame) +//void TRamCachePersist::doGetRaster(int frame, const TRasterP &ras) +{ + Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); + if (it == m_imp->m_chunks.end()) + return TRasterP(); + Imp::CompressedChunk *cc = it->second; + assert(cc); + TRasterP rasP; + m_codec->decompress(cc->m_buffer, cc->m_size, rasP); + return rasP; +} + +//------------------------------------------------------------------------------ + +bool TRamCachePersist::doGetRaster(int frame, TRaster32P &ras) const +{ + Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); + if (it == m_imp->m_chunks.end()) + return false; + Imp::CompressedChunk *cc = it->second; + assert(cc); + TRasterP rasP(ras); + m_codec->decompress(cc->m_buffer, cc->m_size, rasP); + return true; +} + +//------------------------------------------------------------------------------ + +bool TRamCachePersist::doPutRaster(int frame, const TRasterP &ras) +{ + Imp::CompressedChunks::iterator it = m_imp->m_chunks.find(frame); + if (it != m_imp->m_chunks.end()) { + m_imp->m_cacheSize -= it->second->m_size; + delete it->second; + m_imp->m_chunks.erase(it); + } + + UCHAR *outData = 0; + TINT32 outDataSize = 0; + m_codec->compress(ras, 1, &outData, outDataSize); + m_imp->m_cacheSize += outDataSize; + Imp::CompressedChunk *cc = new Imp::CompressedChunk(outData, outDataSize); + m_imp->m_chunks.insert(Imp::CompressedChunks::value_type(frame, cc)); + return true; +} + +//------------------------------------------------------------------------------ + +void TRamCachePersist::onInvalidate() +{ + for (Imp::CompressedChunks::iterator it = m_imp->m_chunks.begin(); + it != m_imp->m_chunks.end(); + ++it) { + Imp::CompressedChunk *cc = it->second; + m_imp->m_cacheSize -= cc->m_size; + delete cc; + } + + m_imp->m_chunks.clear(); +} + +//------------------------------------------------------------------------------ + +void TRamCachePersist::onInvalidate(int startFrame, int endFrame) +{ // ottimizzabile + assert(startFrame <= endFrame); + + for (int frame = startFrame; frame <= endFrame; ++frame) { + Imp::CompressedChunks::iterator it = m_imp->m_chunks.find(frame); + if (it != m_imp->m_chunks.end()) { + m_imp->m_cacheSize -= it->second->m_size; + delete it->second; + m_imp->m_chunks.erase(it); + } + } +} + +//------------------------------------------------------------------------------ + +UCHAR *TRamCachePersist::getRawData(int frame, TINT32 &size, int &lx, int &ly) +{ + Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); + if (it == m_imp->m_chunks.end()) + return 0; + Imp::CompressedChunk *cc = it->second; + assert(cc); + return m_codec->removeHeader(cc->m_buffer, cc->m_size, size, lx, ly); +} + +//------------------------------------------------------------------------------ + +TUINT64 TRamCachePersist::getUsedSpace() +{ + return m_imp->m_cacheSize; +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist::onInvalidate() +{ + //m_imp->m_chunkSize = 0; +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist::onInvalidate(int startFrame, int endFrame) +{ + //m_imp->m_chunkSize = 0; +} + +//------------------------------------------------------------------------------ + +TUINT64 TDiskCachePersist::getUsedSpace() +{ + assert(0); + return 0; +} + +//============================================================================== +//============================================================================== +//============================================================================== +// +// TDiskCachePersist2 +// +//------------------------------------------------------------------------------ +#ifdef WIN32 +namespace +{ + +class ZFile +{ +public: + ZFile(const TFilePath &fp, bool directIO, bool asyncIO); + ~ZFile(); + + void open(); + + int read(BYTE buf[], int size, TINT64 qwOffset) const; + int write(BYTE buf[], int size, TINT64 qwOffset) const; + + void waitForAsyncIOCompletion() const; + + TFilePath getFilePath() const + { + return m_filepath; + } + + int getBytesPerSector() const + { + return m_bytesPerSector; + } + + static void CALLBACK FileIOCompletionRoutine( + DWORD errCode, + DWORD byteTransferred, + LPOVERLAPPED overlapped); + +private: + TFilePath m_filepath; + bool m_directIO; + bool m_asyncIO; + DWORD m_bytesPerSector; + + HANDLE m_fileHandle; + HANDLE m_writeNotPending; +}; + +//----------------------------------------------------------------------------- + +ZFile::ZFile(const TFilePath &fp, bool directIO, bool asyncIO) + : m_filepath(fp), m_directIO(directIO), m_asyncIO(asyncIO), m_fileHandle(0), m_writeNotPending(0) +{ + DWORD sectorsPerCluster; + DWORD numberOfFreeClusters; + DWORD totalNumberOfClusters; + + TFilePathSet disks = TSystem::getDisks(); + + TFilePath disk = fp; + while (std::find(disks.begin(), disks.end(), disk) == disks.end()) + disk = disk.getParentDir(); + + BOOL ret = GetDiskFreeSpaceW( + disk.getWideString().c_str(), // root path + §orsPerCluster, // sectors per cluster + &m_bytesPerSector, // bytes per sector + &numberOfFreeClusters, // free clusters + &totalNumberOfClusters // total clusters + ); + + if (m_asyncIO) + m_writeNotPending = CreateEvent(NULL, TRUE, TRUE, NULL); +} + +//----------------------------------------------------------------------------- + +ZFile::~ZFile() +{ + if (m_fileHandle) + CloseHandle(m_fileHandle); + + if (m_writeNotPending) + CloseHandle(m_writeNotPending); +} + +//----------------------------------------------------------------------------- + +void ZFile::open() +{ + DWORD flagsAndAttributes = 0; + flagsAndAttributes = m_directIO ? FILE_FLAG_NO_BUFFERING : 0UL; + flagsAndAttributes |= m_asyncIO ? FILE_FLAG_OVERLAPPED : 0UL; + + // Open the file for write access. + m_fileHandle = CreateFileW(m_filepath.getWideString().c_str(), + GENERIC_READ | GENERIC_WRITE, // Read/Write access + 0, // no sharing allowed + NULL, // no security + OPEN_ALWAYS, // open it or create new if it doesn't exist + flagsAndAttributes, + NULL); // ignored + + if (m_fileHandle == INVALID_HANDLE_VALUE) { + m_fileHandle = 0; + + char errorMessage[2048]; + + DWORD error = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, NULL); + + throw TException(errorMessage); + } +} + +//----------------------------------------------------------------------------- + +int ZFile::read(BYTE buf[], int size, TINT64 qwOffset) const +{ + assert(size % m_bytesPerSector == 0); + assert(qwOffset % m_bytesPerSector == 0); + + char msg[2048] = ""; + unsigned long bytesToRead; // Padded number of bytes to read. + unsigned long bytesRead; // count of bytes actually read + + OVERLAPPED overLapped; + memset(&overLapped, 0, sizeof(overLapped)); + +#define DWORDLONG_LO_DWORD(dwl64) ((DWORD)(dwl64)) +#define DWORDLONG_HI_DWORD(dwl64) ((DWORD)(dwl64 >> 32)) + + // set the overlapped stucture with the offsets + overLapped.Offset = DWORDLONG_LO_DWORD(qwOffset); + overLapped.OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); + + if (m_asyncIO) { + overLapped.hEvent = CreateEvent( + NULL, // SD + TRUE, // manual reset + FALSE, // initial state is not signaled + NULL); // object name + } else + overLapped.hEvent = NULL; + + bytesToRead = size; + + // Read a bunch of bytes and store in buf + int result = ReadFile( + m_fileHandle, // file handle + (void *)buf, // buffer to store data + bytesToRead, // num bytes to read + &bytesRead, // bytes read + &overLapped); // stucture for file offsets + + if (!result) { + DWORD error = GetLastError(); + if (m_asyncIO && ERROR_IO_PENDING == error) { + if (!GetOverlappedResult(m_fileHandle, &overLapped, &bytesRead, TRUE)) { + char errorMessage[2048]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, NULL); + + throw TException(errorMessage); + } + } else { + char errorMessage[2048]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, NULL); + + throw TException(errorMessage); + } + } + + return bytesRead; +} + +//----------------------------------------------------------------------------- + +int ZFile::write(BYTE buf[], int size, TINT64 qwOffset) const +{ + assert(size % m_bytesPerSector == 0); + assert(qwOffset % m_bytesPerSector == 0); + + char msg[2048] = ""; + unsigned long bytesToWrite; // Padded number of bytes to write. + unsigned long bytesWritten = 0; // count of bytes actually writtten + + int result; + + if (m_asyncIO) { + OVERLAPPED *overLapped = new OVERLAPPED; + memset(overLapped, 0, sizeof(OVERLAPPED)); + + // set the overlapped stucture with the offsets + overLapped->Offset = DWORDLONG_LO_DWORD(qwOffset); + overLapped->OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); + overLapped->hEvent = NULL; + + bytesToWrite = size; + + result = WriteFileEx( + m_fileHandle, // file handle + (void *)buf, // data buffer + bytesToWrite, // num bytes to write + overLapped, // stucture for file offsets + &ZFile::FileIOCompletionRoutine); + + ResetEvent(m_writeNotPending); + } else { + OVERLAPPED overLapped; + memset(&overLapped, 0, sizeof(overLapped)); + + // set the overlapped stucture with the offsets + overLapped.Offset = DWORDLONG_LO_DWORD(qwOffset); + overLapped.OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); + overLapped.hEvent = NULL; + + bytesToWrite = size; + + result = WriteFile( + m_fileHandle, // file handle + (void *)buf, // data buffer + bytesToWrite, // num bytes to read + &bytesWritten, // bytes read + &overLapped); // stucture for file offsets + } + + if (!result) { + char errorMessage[2048]; + DWORD error = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, NULL); + + throw TException(errorMessage); + } + + return bytesWritten; +} + +//------------------------------------------------------------------------------ + +void ZFile::waitForAsyncIOCompletion() const +{ + if (m_asyncIO) { + WaitForSingleObjectEx(m_writeNotPending, INFINITE, TRUE); + SetEvent(m_writeNotPending); + } +} + +//------------------------------------------------------------------------------ + +void CALLBACK ZFile::FileIOCompletionRoutine( + DWORD errCode, + DWORD byteTransferred, + LPOVERLAPPED overlapped) +{ + delete overlapped; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class BufferQueue +{ +public: + class Item + { + public: + Item(int frame, UCHAR *buffer, int bufferSize, int chunkSize) + : m_frame(frame), m_buffer(buffer), m_bufferSize(bufferSize), m_chunkSize(chunkSize) + { + } + + int m_frame; + UCHAR *m_buffer; + int m_bufferSize; + TINT64 m_chunkSize; + }; + + BufferQueue(int capacity, int allocUnit) + : m_capacity(capacity), m_allocUnit(allocUnit), m_notEmpty(), m_notFull(), m_mutex(), m_bufferCount(0), m_nextPutItem(0), m_nextGetItem(0) + { + for (int i = 0; i < m_capacity; ++i) + m_items.push_back(Item(-1, (UCHAR *)0, 0, 0)); + } + + ~BufferQueue() + { + for (int i = 0; i < m_capacity; ++i) + delete[] m_items[i].m_buffer; + } + + void put(int frame, UCHAR *buffer, int bufferSize, int chunkSize) + { + TThread::ScopedLock sl(m_mutex); + while (m_bufferCount == m_capacity) + m_notFull.wait(sl); + + if (m_items[m_nextPutItem].m_chunkSize != chunkSize) { + delete[] m_items[m_nextPutItem].m_buffer; + m_items[m_nextPutItem].m_buffer = new UCHAR[chunkSize]; + m_items[m_nextPutItem].m_chunkSize = chunkSize; + } + + memcpy(m_items[m_nextPutItem].m_buffer, buffer, bufferSize); + m_items[m_nextPutItem].m_frame = frame; + + m_nextPutItem = (m_nextPutItem + 1) % m_capacity; + ++m_bufferCount; + m_notEmpty.notifyOne(); + } + + BufferQueue::Item get() + { + TThread::ScopedLock sl(m_mutex); + + while (m_bufferCount == 0) + m_notEmpty.wait(sl); + + m_notFull.notifyOne(); + + BufferQueue::Item item = m_items[m_nextGetItem]; + + m_nextGetItem = (m_nextGetItem + 1) % m_capacity; + --m_bufferCount; + return item; + } + + void saveOne(ZFile *file) + { + TThread::ScopedLock sl(m_mutex); + + while (m_bufferCount == 0) + m_notEmpty.wait(sl); + + m_notFull.notifyOne(); + + BufferQueue::Item item = m_items[m_nextGetItem]; + + m_nextGetItem = (m_nextGetItem + 1) % m_capacity; + --m_bufferCount; + + TINT64 pos = item.m_frame * item.m_chunkSize; + TINT64 sectorCount = pos / m_allocUnit; + + if ((pos % m_allocUnit) != 0) + ++sectorCount; + + pos = sectorCount * m_allocUnit; + file->write(item.m_buffer, (TINT32)item.m_chunkSize, pos); + } + + int size() + { + TThread::ScopedLock sl(m_mutex); + return m_bufferCount; + } + +private: + int m_capacity; + int m_allocUnit; + + TThread::Condition m_notEmpty; + TThread::Condition m_notFull; + TThread::Mutex m_mutex; + + vector m_items; + + int m_bufferCount; + int m_nextPutItem; + int m_nextGetItem; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class WriteBufferTask : public TThread::Runnable +{ +public: + WriteBufferTask(ZFile *file, BufferQueue *bufferQueue) + : m_file(file), m_bufferQueue(bufferQueue) + { + } + + void run(); + + ZFile *m_file; + BufferQueue *m_bufferQueue; +}; + +//------------------------------------------------------------------------------ + +void WriteBufferTask::run() +{ + while (true) { + TThread::milestone(); + + try { + m_bufferQueue->saveOne(m_file); + m_file->waitForAsyncIOCompletion(); + } catch (TException & /*e*/) { + } catch (...) { + } + } +} + +} // anonymous namespace + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +class TDiskCachePersist2::Imp +{ +public: + Imp(const TFilePath &fp, bool asyncWrite) + : m_chunkSize(0), m_readBuffer(0), m_file(new ZFile(fp, true, asyncWrite)), m_asyncWrite(asyncWrite), m_executor(0), m_bufferQueue(0) + { + m_file->open(); + m_allocUnit = m_file->getBytesPerSector(); + + if (m_asyncWrite) { + m_executor = new TThread::Executor(); + m_bufferQueue = new BufferQueue(4, m_allocUnit); + m_executor->addTask(new WriteBufferTask(m_file, m_bufferQueue)); + } + } + + ~Imp() + { + delete m_file; + delete[] m_readBuffer; + + if (m_executor) { + m_executor->cancel(); + delete m_executor; + } + + delete m_bufferQueue; + } + + bool put(int frame, UCHAR *data, TUINT32 dataSize); + UCHAR *get(int pos, TUINT32 *size); + + TThread::Mutex m_mutex; + TINT64 m_chunkSize; + + ZFile *m_file; + int m_allocUnit; + UCHAR *m_readBuffer; + int m_lx; + int m_ly; + int m_bpp; + + bool m_asyncWrite; + TThread::Executor *m_executor; + BufferQueue *m_bufferQueue; +}; + +//------------------------------------------------------------------------------ + +bool TDiskCachePersist2::Imp::put(int frame, UCHAR *data, TUINT32 dataSize) +{ + if (dataSize != m_chunkSize) + return false; + + TINT64 pos = frame * m_chunkSize; + TINT64 sectorCount = pos / m_allocUnit; + + if ((pos % m_allocUnit) != 0) + ++sectorCount; + + pos = sectorCount * m_allocUnit; + + m_file->write(data, dataSize, pos); + return true; +} + +//------------------------------------------------------------------------------ + +UCHAR *TDiskCachePersist2::Imp::get(int frame, TUINT32 *size) +{ + UCHAR *ret = new UCHAR[TINT32(m_chunkSize)]; + + TThread::ScopedLock sl(m_mutex); + + TINT64 pos = frame * m_chunkSize; + TINT64 sectorCount = pos / m_allocUnit; + + if ((pos % m_allocUnit) != 0) + ++sectorCount; + + pos = sectorCount * m_allocUnit; + + m_file->read(ret, TINT32(m_chunkSize), pos); + *size = TUINT32(m_chunkSize); + return ret; +} + +//------------------------------------------------------------------------------ + +TDiskCachePersist2::TDiskCachePersist2(TRasterCodec *codec, const TFilePath &fullpath) + : TCachePersist(codec), m_imp(new Imp(fullpath, false /*true*/)) +{ +} + +//------------------------------------------------------------------------------ + +TDiskCachePersist2::~TDiskCachePersist2() +{ + delete m_imp; +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist2::setFrameSize(int lx, int ly, int bpp) +{ + m_imp->m_lx = lx; + m_imp->m_ly = ly; + m_imp->m_bpp = bpp; + + // inizializza m_imp->m_chunkSize in modo che sia un multiplo di m_imp->m_allocUnit + if (m_codec) + m_imp->m_chunkSize = m_codec->getMaxCompressionSize(lx * ly * (bpp >> 3)); + else + m_imp->m_chunkSize = lx * ly * (bpp >> 3); + + TINT64 allocUnitCount = m_imp->m_chunkSize / m_imp->m_allocUnit; + if ((m_imp->m_chunkSize % m_imp->m_allocUnit) != 0) + ++allocUnitCount; + + m_imp->m_chunkSize = allocUnitCount * m_imp->m_allocUnit; + delete[] m_imp->m_readBuffer; + m_imp->m_readBuffer = 0; +} + +//------------------------------------------------------------------------------ + +TRasterP TDiskCachePersist2::doGetRaster(int frame) +{ + TRasterP outRas; + TUINT32 size; + + if (!m_imp->m_readBuffer) + m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; + + TINT64 pos = frame * m_imp->m_chunkSize; + TINT64 sectorCount = pos / m_imp->m_allocUnit; + + if ((pos % m_imp->m_allocUnit) != 0) + ++sectorCount; + + pos = sectorCount * m_imp->m_allocUnit; + + m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); + size = TUINT32(m_imp->m_chunkSize); + + if (m_codec) + m_codec->decompress(m_imp->m_readBuffer, size, outRas); + else { + switch (m_imp->m_bpp) { + case 32: { + TRaster32P ras(m_imp->m_lx, m_imp->m_ly); + outRas = ras; + } break; + case 64: { + TRaster64P ras(m_imp->m_lx, m_imp->m_ly); + outRas = ras; + } break; + default: + throw TException("unsupported pixel format"); + break; + } + + unsigned int rasSize = outRas->getRowSize() * outRas->getLy(); + assert(size >= rasSize); + outRas->lock(); + memcpy(outRas->getRawData(), m_imp->m_readBuffer, rasSize); + outRas->unlock(); + } + return outRas; +} + +//------------------------------------------------------------------------------ + +bool TDiskCachePersist2::doGetRaster(int frame, TRaster32P &ras) const +{ + if (!m_imp->m_readBuffer) + m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; + + TINT64 pos = frame * m_imp->m_chunkSize; + TINT64 sectorCount = pos / m_imp->m_allocUnit; + + if ((pos % m_imp->m_allocUnit) != 0) + ++sectorCount; + + pos = sectorCount * m_imp->m_allocUnit; + + TRasterP rasP = ras; + if (m_codec) { + m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); + m_codec->decompress(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), rasP); + assert(rasP->getSize() == ras->getSize()); + ras->copy(rasP); + } else { + assert(ras->getLx() == ras->getWrap()); + int rasSize = ras->getRowSize() * ras->getLy(); + ras->lock(); + if (rasSize == m_imp->m_chunkSize) + m_imp->m_file->read(ras->getRawData(), TINT32(m_imp->m_chunkSize), pos); + else { + assert(rasSize < m_imp->m_chunkSize); + m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); + memcpy(ras->getRawData(), m_imp->m_readBuffer, rasSize); + } + ras->unlock(); + } + return true; +} + +//------------------------------------------------------------------------------ + +bool TDiskCachePersist2::doPutRaster(int frame, const TRasterP &inRas) +{ + UCHAR *outData = 0; + TINT32 outDataSize = 0; + int actualDataSize = 0; + + bool deleteDataBuffer = false; + if (m_codec) { + m_codec->compress(inRas, m_imp->m_allocUnit, &outData, outDataSize); + deleteDataBuffer = true; + ; + actualDataSize = outDataSize; + } else { + assert(inRas->getLx() == inRas->getWrap()); + int rasSize = inRas->getLx() * inRas->getLy() * inRas->getPixelSize(); + + outDataSize = TINT32(m_imp->m_chunkSize); + inRas->lock(); + if (rasSize == m_imp->m_chunkSize) + outData = inRas->getRawData(); + else { + if (!m_imp->m_readBuffer) + m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; + + memcpy(m_imp->m_readBuffer, inRas->getRawData(), rasSize); + outData = m_imp->m_readBuffer; + } + inRas->unlock(); + actualDataSize = rasSize; + } + + assert((outDataSize % m_imp->m_allocUnit) == 0); + + bool cached = true; + if (m_imp->m_asyncWrite) + m_imp->m_bufferQueue->put(frame, outData, actualDataSize, outDataSize); + else + cached = m_imp->put(frame, outData, outDataSize); + + if (deleteDataBuffer) + delete[] outData; + return cached; +} + +//------------------------------------------------------------------------------ + +UCHAR *TDiskCachePersist2::getRawData(int frame, TINT32 &size, int &lx, int &ly) +{ + TUINT32 inDataSize; + UCHAR *src = m_imp->get(frame, &inDataSize); + return m_codec->removeHeader(src, inDataSize, size, lx, ly); +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist2::onInvalidate() +{ +} + +//------------------------------------------------------------------------------ + +void TDiskCachePersist2::onInvalidate(int startFrame, int endFrame) +{ +} + +//------------------------------------------------------------------------------ + +TUINT64 TDiskCachePersist2::getUsedSpace() +{ + TFileStatus fs(m_imp->m_file->getFilePath()); + return fs.getSize(); +} + +//------------------------------------------------------------------------------ +#endif //WIN32 diff --git a/toonz/sources/common/trasterimage/tcodec.cpp b/toonz/sources/common/trasterimage/tcodec.cpp new file mode 100644 index 0000000..3d32c7b --- /dev/null +++ b/toonz/sources/common/trasterimage/tcodec.cpp @@ -0,0 +1,810 @@ + + +#include "tcodec.h" +#include "trastercm.h" +#include "tpixel.h" +#include "tbigmemorymanager.h" +#include "tsystem.h" +//#include "tstopwatch.h" +#include "timagecache.h" +#include "trasterimage.h" + +//#include "snappy-c.h" +#include "lz4frame_static.h" +#include +#include +#include + +using namespace std; + +namespace +{ +class Header +{ + enum RasType { + Raster32RGBM, + Raster64RGBM, + Raster32CM, + RasterGR8, + RasterGR16, + RasterUnknown + }; + +public: + Header(const TRasterP &ras); + ~Header() {} + TRasterP createRaster() const; + int getRasterSize() const; + int m_lx; + int m_ly; + RasType m_rasType; + Header(void *mem) { memcpy(this, mem, sizeof(Header)); } + +private: + Header(); // not implemented +}; + +//------------------------------------------------------------------------------ + +Header::Header(const TRasterP &ras) +{ + assert(ras); + m_lx = ras->getLx(); + m_ly = ras->getLy(); + TRaster32P ras32(ras); + if (ras32) + m_rasType = Raster32RGBM; + else { + TRasterCM32P rasCM32(ras); + if (rasCM32) + m_rasType = Raster32CM; + else { + TRaster64P ras64(ras); + if (ras64) + m_rasType = Raster64RGBM; + else { + TRasterGR8P rasGR8(ras); + if (rasGR8) + m_rasType = RasterGR8; + else { + TRasterGR16P rasGR16(ras); + if (rasGR16) + m_rasType = RasterGR16; + else { + assert(!"Unknown RasterType"); + m_rasType = RasterUnknown; + } + } + } + } + } +} + +//------------------------------------------------------------------------------ + +TRasterP Header::createRaster() const +{ + switch (m_rasType) { + case Raster32RGBM: + return TRaster32P(m_lx, m_ly); + break; + case Raster32CM: + return TRasterCM32P(m_lx, m_ly); + break; + case Raster64RGBM: + return TRaster64P(m_lx, m_ly); + break; + case RasterGR8: + return TRasterGR8P(m_lx, m_ly); + break; + case RasterGR16: + return TRasterGR16P(m_lx, m_ly); + break; + default: + assert(0); + return TRasterP(); + break; + } + return TRasterP(); +} + +//------------------------------------------------------------------------------ + +int Header::getRasterSize() const +{ + switch (m_rasType) { + case Raster32RGBM: + return 4 * m_lx * m_ly; + break; + case Raster32CM: + return 4 * m_lx * m_ly; + break; + case Raster64RGBM: + return 8 * m_lx * m_ly; + break; + case RasterGR8: + return m_lx * m_ly; + break; + default: + assert(0); + return 0; + break; + } +} +//------------------------------------------------------------------------------ +} //anonymous namespace + +//------------------------------------------------------------------------------ +// TRasterCodecSnappy +//------------------------------------------------------------------------------ + +/*TRasterCodecSnappy::TRasterCodecSnappy(const string &name, bool useCache) + : TRasterCodec(name) + , m_raster() + , m_useCache(useCache) + , m_cacheId("") +{ +} + +//------------------------------------------------------------------------------ + +TRasterCodecSnappy::~TRasterCodecSnappy() +{ + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + else + m_raster = TRasterGR8P(); +} + +//------------------------------------------------------------------------------ + +UINT TRasterCodecSnappy::doCompress(const TRasterP &inRas, int allocUnit, TRasterGR8P& outRas) +{ + assert(inRas); + + assert(inRas->getLx() == inRas->getWrap()); + + + size_t inDataSize = inRas->getLx() * inRas->getLy() * inRas->getPixelSize(); + size_t maxReqSize = snappy_max_compressed_length(inDataSize); + + if (m_useCache) + { + if (m_cacheId=="") + m_cacheId = TImageCache::instance()->getUniqueId(); + else + outRas = ((TRasterImageP)TImageCache::instance()->get(m_cacheId, true))->getRaster(); + } + else + outRas = m_raster; + + if (!outRas || outRas->getLx()<(int)maxReqSize) + { + outRas = TRasterGR8P(); + m_raster = TRasterGR8P(); + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + outRas = TRasterGR8P(maxReqSize, 1); + if (m_useCache) + TImageCache::instance()->add(m_cacheId, TRasterImageP(outRas), true); + else + m_raster = outRas; + } + + outRas->lock(); + char* buffer = (char*) outRas->getRawData(); + if (!buffer) + return 0; + + inRas->lock(); + char* inData = (char*) inRas->getRawData(); + + size_t outSize = maxReqSize; + snappy_status r = snappy_compress(inData, inDataSize, buffer, &outSize); + + outRas->unlock(); + inRas->unlock(); + + if(r != SNAPPY_OK) + throw TException("compress... something goes bad"); + + return outSize; + } + +//------------------------------------------------------------------------------ + +TRasterP TRasterCodecSnappy::compress(const TRasterP &inRas, int allocUnit, TINT32 &outDataSize) +{ + TRasterGR8P rasOut; + UINT outSize = doCompress(inRas, allocUnit, rasOut); + if (outSize==0) + return TRasterP(); + + UINT headerSize = sizeof(Header); + if (TBigMemoryManager::instance()->isActive() && + TBigMemoryManager::instance()->getAvailableMemoryinKb()<((outSize + headerSize)>>10)) + return TRasterP(); + + TRasterGR8P r8(outSize + headerSize, 1); + r8->lock(); + UCHAR *memoryChunk = r8->getRawData(); + if (!memoryChunk) + return TRasterP(); + Header head(inRas); + + memcpy(memoryChunk, &head, headerSize); + UCHAR *tmp = memoryChunk + headerSize; + rasOut->lock(); + memcpy(tmp, rasOut->getRawData(), outSize); + r8->unlock(); + rasOut->unlock(); + outDataSize = outSize + headerSize; + return r8; +} + +//------------------------------------------------------------------------------ + +bool TRasterCodecSnappy::decompress(const UCHAR* inData, TINT32 inDataSize, TRasterP &outRas, bool safeMode) +{ + int headerSize = sizeof(Header); + + Header *header= (Header *)inData; + if (!outRas) + { + outRas = header->createRaster(); + if (!outRas) + throw TException(); + } + else + { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + int outDataSize = header->getRasterSize(); + + char* mc = (char*) inData + headerSize; + int ds = inDataSize - headerSize; + + size_t outSize; + snappy_uncompressed_length(mc, ds, &outSize); + + outRas->lock(); + snappy_status rc = snappy_uncompress(mc, ds, (char*) outRas->getRawData(), &outSize); + outRas->unlock(); + + if (rc != SNAPPY_OK) + { + if (safeMode) + return false; + else + { + throw TException("decompress... something goes bad"); + return false; + } + } + + assert(outSize == (size_t)outDataSize); + return true; +} + +//------------------------------------------------------------------------------ + +void TRasterCodecSnappy::decompress(const TRasterP & compressedRas, TRasterP &outRas) +{ + int headerSize = sizeof(Header); + + assert(compressedRas->getLy()==1 && compressedRas->getPixelSize()==1); + UINT inDataSize = compressedRas->getLx(); + + compressedRas->lock(); + + UCHAR* inData = compressedRas->getRawData(); + Header header(inData); + + if (!outRas) + { + outRas = header.createRaster(); + if (!outRas) + throw TException(); + } + else + { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + int outDataSize = header.getRasterSize(); + + char* mc = (char*) inData + headerSize; + int ds = inDataSize - headerSize; + + size_t outSize; + snappy_uncompressed_length(mc, ds, &outSize); + + char* outData = (char*) outRas->getRawData(); + + outRas->lock(); + + snappy_status rc = snappy_uncompress(mc, ds, outData, &outSize); + + outRas->unlock(); + compressedRas->unlock(); + + if (rc != SNAPPY_OK) + throw TException("decompress... something goes bad"); + + assert(outSize == (size_t)outDataSize); +}*/ + +//------------------------------------------------------------------------------ +// TRasterCodecLz4 +//------------------------------------------------------------------------------ + +namespace +{ +bool lz4decompress(LZ4F_decompressionContext_t lz4dctx, + char *out, size_t *out_len_res, + const char *in, size_t in_len) +{ + size_t out_len = *out_len_res, + in_read, out_written; + + *out_len_res = 0; + + while (in_len) { + out_written = out_len; + in_read = in_len; + + size_t res = LZ4F_decompress( + lz4dctx, (void *)out, &out_written, (const void *)in, &in_read, NULL); + + if (LZ4F_isError(res)) + return false; + + *out_len_res += out_written; + + out += out_written; + out_len -= out_written; + + in += in_read; + in_len -= in_read; + } + + return true; +} +} // namespace + +TRasterCodecLz4::TRasterCodecLz4(const string &name, bool useCache) + : TRasterCodec(name), m_raster(), m_useCache(useCache), m_cacheId("") +{ +} + +//------------------------------------------------------------------------------ + +TRasterCodecLz4::~TRasterCodecLz4() +{ + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + else + m_raster = TRasterGR8P(); +} + +//------------------------------------------------------------------------------ + +UINT TRasterCodecLz4::doCompress(const TRasterP &inRas, int allocUnit, TRasterGR8P &outRas) +{ + assert(inRas); + + assert(inRas->getLx() == inRas->getWrap()); + + size_t inDataSize = inRas->getLx() * inRas->getLy() * inRas->getPixelSize(); + size_t maxReqSize = LZ4F_compressFrameBound(inDataSize, NULL); + + if (m_useCache) { + if (m_cacheId == "") + m_cacheId = TImageCache::instance()->getUniqueId(); + else + outRas = ((TRasterImageP)TImageCache::instance()->get(m_cacheId, true))->getRaster(); + } else + outRas = m_raster; + + if (!outRas || outRas->getLx() < (int)maxReqSize) { + outRas = TRasterGR8P(); + m_raster = TRasterGR8P(); + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + outRas = TRasterGR8P(maxReqSize, 1); + if (m_useCache) + TImageCache::instance()->add(m_cacheId, TRasterImageP(outRas), true); + else + m_raster = outRas; + } + + outRas->lock(); + void *buffer = (void *)outRas->getRawData(); + if (!buffer) + return 0; + + inRas->lock(); + const void *inData = (const void *)inRas->getRawData(); + + size_t outSize = LZ4F_compressFrame(buffer, maxReqSize, inData, inDataSize, NULL); + outRas->unlock(); + inRas->unlock(); + + if (LZ4F_isError(outSize)) + throw TException("compress... something goes bad"); + + return outSize; +} + +//------------------------------------------------------------------------------ + +TRasterP TRasterCodecLz4::compress(const TRasterP &inRas, int allocUnit, TINT32 &outDataSize) +{ + TRasterGR8P rasOut; + UINT outSize = doCompress(inRas, allocUnit, rasOut); + if (outSize == 0) + return TRasterP(); + + UINT headerSize = sizeof(Header); + if (TBigMemoryManager::instance()->isActive() && + TBigMemoryManager::instance()->getAvailableMemoryinKb() < ((outSize + headerSize) >> 10)) + return TRasterP(); + + TRasterGR8P r8(outSize + headerSize, 1); + r8->lock(); + UCHAR *memoryChunk = r8->getRawData(); + if (!memoryChunk) + return TRasterP(); + Header head(inRas); + + memcpy(memoryChunk, &head, headerSize); + UCHAR *tmp = memoryChunk + headerSize; + rasOut->lock(); + memcpy(tmp, rasOut->getRawData(), outSize); + r8->unlock(); + rasOut->unlock(); + outDataSize = outSize + headerSize; + return r8; +} + +//------------------------------------------------------------------------------ + +bool TRasterCodecLz4::decompress(const UCHAR *inData, TINT32 inDataSize, TRasterP &outRas, bool safeMode) +{ + int headerSize = sizeof(Header); + + Header *header = (Header *)inData; + if (!outRas) { + outRas = header->createRaster(); + if (!outRas) + throw TException(); + } else { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + LZ4F_decompressionContext_t lz4dctx; + + LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION); + if (LZ4F_isError(err)) + throw TException("compress... something goes bad"); + + int outDataSize = header->getRasterSize(); + + const char *mc = (const char *)(inData + headerSize); + size_t ds = inDataSize - headerSize; + + size_t outSize = outDataSize; + char *outData = (char *)outRas->getRawData(); + + outRas->lock(); + //err = LZ4F_decompress(lz4dctx, outData, &outSize, mc, &ds, NULL); + bool ok = lz4decompress(lz4dctx, outData, &outSize, mc, ds); + LZ4F_freeDecompressionContext(lz4dctx); + outRas->unlock(); + + if (!ok) { + if (safeMode) + return false; + else { + throw TException("decompress... something goes bad"); + return false; + } + } + + assert(outSize == (size_t)outDataSize); + return true; +} + +//------------------------------------------------------------------------------ + +void TRasterCodecLz4::decompress(const TRasterP &compressedRas, TRasterP &outRas) +{ + int headerSize = sizeof(Header); + + assert(compressedRas->getLy() == 1 && compressedRas->getPixelSize() == 1); + UINT inDataSize = compressedRas->getLx(); + + compressedRas->lock(); + + UCHAR *inData = compressedRas->getRawData(); + Header header(inData); + + if (!outRas) { + outRas = header.createRaster(); + if (!outRas) + throw TException(); + } else { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + LZ4F_decompressionContext_t lz4dctx; + + LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION); + if (LZ4F_isError(err)) + throw TException("compress... something goes bad"); + + int outDataSize = header.getRasterSize(); + + const char *mc = (const char *)(inData + headerSize); + size_t ds = inDataSize - headerSize; + + size_t outSize = outDataSize; + char *outData = (char *)outRas->getRawData(); + + outRas->lock(); + + //err = LZ4F_decompress(lz4dctx, outData, &outSize, mc, &ds, NULL); + bool ok = lz4decompress(lz4dctx, outData, &outSize, mc, ds); + LZ4F_freeDecompressionContext(lz4dctx); + + outRas->unlock(); + compressedRas->unlock(); + + if (!ok) + throw TException("decompress... something goes bad"); + + assert(outSize == (size_t)outDataSize); +} + +//------------------------------------------------------------------------------ +// TRasterCodecLZO +//------------------------------------------------------------------------------ + +namespace +{ + +bool lzoCompress(const QByteArray src, QByteArray &dst) +{ + QDir exeDir(QCoreApplication::applicationDirPath()); + QString compressExe = exeDir.filePath("lzocompress"); + QProcess process; + process.start(compressExe, QStringList() << QString::number(src.size())); + if (!process.waitForStarted()) + return false; + process.write(src); + if (!process.waitForFinished()) + return false; + dst = process.readAll(); + return process.exitCode() == 0; +} + +bool lzoDecompress(const QByteArray src, int dstSize, QByteArray &dst) +{ + QDir exeDir(QCoreApplication::applicationDirPath()); + QString decompressExe = exeDir.filePath("lzodecompress"); + QProcess process; + process.start(decompressExe, QStringList() << QString::number(dstSize) << QString::number(src.size())); + if (!process.waitForStarted()) + return false; + process.write(src); + if (!process.waitForFinished()) + return false; + dst = process.readAll(); + return process.exitCode() == 0 && dst.size() == dstSize; +} +} + +//------------------------------------------------------------------------------ + +TRasterCodecLZO::TRasterCodecLZO(const string &name, bool useCache) + : TRasterCodec(name), m_raster(), m_useCache(useCache), m_cacheId("") +{ +} + +//------------------------------------------------------------------------------ + +TRasterCodecLZO::~TRasterCodecLZO() +{ + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + else + m_raster = TRasterGR8P(); +} + +//------------------------------------------------------------------------------ + +UINT TRasterCodecLZO::doCompress(const TRasterP &inRas, int allocUnit, TRasterGR8P &outRas) +{ + assert(inRas); + + assert(inRas->getLx() == inRas->getWrap()); + + size_t inDataSize = inRas->getLx() * inRas->getLy() * inRas->getPixelSize(); + + // compress data + inRas->lock(); + char *inData = (char *)inRas->getRawData(); + QByteArray compressedBuffer; + if (!lzoCompress(QByteArray(inData, inDataSize), compressedBuffer)) + throw TException("LZO compression failed"); + + inRas->unlock(); + + size_t maxReqSize = compressedBuffer.size(); // we have just done the compression: we know the actual size + + if (m_useCache) { + if (m_cacheId == "") + m_cacheId = TImageCache::instance()->getUniqueId(); + else + outRas = ((TRasterImageP)TImageCache::instance()->get(m_cacheId, true))->getRaster(); + } else + outRas = m_raster; + + if (!outRas || outRas->getLx() < (int)maxReqSize) { + outRas = TRasterGR8P(); + m_raster = TRasterGR8P(); + if (m_useCache) + TImageCache::instance()->remove(m_cacheId); + outRas = TRasterGR8P(maxReqSize, 1); + if (m_useCache) + TImageCache::instance()->add(m_cacheId, TRasterImageP(outRas), true); + else + m_raster = outRas; + } + + size_t outSize = maxReqSize; + outRas->lock(); + char *buffer = (char *)outRas->getRawData(); // Change cast types, if needed + if (!buffer) { + outRas->unlock(); + return 0; + } + memcpy(buffer, compressedBuffer.data(), outSize); + outRas->unlock(); + + return outSize; +} + +//------------------------------------------------------------------------------ + +TRasterP TRasterCodecLZO::compress(const TRasterP &inRas, int allocUnit, TINT32 &outDataSize) +{ + TRasterGR8P rasOut; + UINT outSize = doCompress(inRas, allocUnit, rasOut); + if (outSize == 0) + return TRasterP(); + + UINT headerSize = sizeof(Header); + if (TBigMemoryManager::instance()->isActive() && + TBigMemoryManager::instance()->getAvailableMemoryinKb() < ((outSize + headerSize) >> 10)) + return TRasterP(); + + TRasterGR8P r8(outSize + headerSize, 1); + r8->lock(); + UCHAR *memoryChunk = r8->getRawData(); + if (!memoryChunk) + return TRasterP(); + Header head(inRas); + + memcpy(memoryChunk, &head, headerSize); + UCHAR *tmp = memoryChunk + headerSize; + rasOut->lock(); + memcpy(tmp, rasOut->getRawData(), outSize); + r8->unlock(); + rasOut->unlock(); + outDataSize = outSize + headerSize; + return r8; +} + +//------------------------------------------------------------------------------ + +bool TRasterCodecLZO::decompress(const UCHAR *inData, TINT32 inDataSize, TRasterP &outRas, bool safeMode) +{ + int headerSize = sizeof(Header); + + Header *header = (Header *)inData; + if (!outRas) { + outRas = header->createRaster(); + if (!outRas) + throw TException(); + } else { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + int outDataSize = header->getRasterSize(); + + char *mc = (char *)inData + headerSize; + int ds = inDataSize - headerSize; + + size_t outSize = outDataSize; // Calculate output buffer size + + QByteArray decompressedBuffer; + if (!lzoDecompress(QByteArray(mc, ds), outSize, decompressedBuffer)) + throw TException("LZO decompression failed"); + + outRas->lock(); + memcpy(outRas->getRawData(), decompressedBuffer.data(), decompressedBuffer.size()); + bool rc = true; + + outRas->unlock(); + + /* + if (rc != true) // Check success code here + { + if (safeMode) + return false; + else + { + throw TException("decompress... something goes bad"); + return false; + } + } + */ + + assert(outSize == (size_t)outDataSize); + return true; +} + +//------------------------------------------------------------------------------ + +void TRasterCodecLZO::decompress(const TRasterP &compressedRas, TRasterP &outRas) +{ + int headerSize = sizeof(Header); + + assert(compressedRas->getLy() == 1 && compressedRas->getPixelSize() == 1); + UINT inDataSize = compressedRas->getLx(); + + compressedRas->lock(); + + UCHAR *inData = compressedRas->getRawData(); + Header header(inData); + + if (!outRas) { + outRas = header.createRaster(); + if (!outRas) + throw TException(); + } else { + if (outRas->getLx() != outRas->getWrap()) + throw TException(); + } + + int outDataSize = header.getRasterSize(); + + char *mc = (char *)inData + headerSize; + int ds = inDataSize - headerSize; + + size_t outSize = outDataSize; // Calculate output buffer size + + char *outData = (char *)outRas->getRawData(); + + QByteArray decompressedBuffer; + if (!lzoDecompress(QByteArray(mc, ds), outSize, decompressedBuffer)) + throw TException("LZO decompression failed"); + outRas->lock(); + memcpy(outRas->getRawData(), decompressedBuffer.data(), decompressedBuffer.size()); + bool rc = true; + + outRas->unlock(); + compressedRas->unlock(); + + if (rc != true) // Check success code here + throw TException("decompress... something goes bad"); + + assert(outSize == (size_t)outDataSize); +} diff --git a/toonz/sources/common/trasterimage/trasterimage.cpp b/toonz/sources/common/trasterimage/trasterimage.cpp new file mode 100644 index 0000000..ecfeb24 --- /dev/null +++ b/toonz/sources/common/trasterimage/trasterimage.cpp @@ -0,0 +1,95 @@ + + +#include "trasterimage.h" +#include "trop.h" + +//--------------------------------------------------------- + +TRasterImage::TRasterImage() + : m_mainRaster(), m_patchRaster(), m_iconRaster(), m_dpix(0), m_dpiy(0), m_name(""), m_savebox() + //, m_hPos(0.0) + , + m_isOpaque(false), m_isScanBW(false), m_offset(0, 0), m_subsampling(1) +{ +} + +//--------------------------------------------------------- + +TRasterImage::TRasterImage(const TRasterP &ras) + : m_mainRaster(ras), m_patchRaster(), m_iconRaster(), m_dpix(0), m_dpiy(0), m_name(""), m_savebox(0, 0, ras->getLx() - 1, ras->getLy() - 1) + //, m_hPos(0.0) + , + m_isOpaque(false), m_isScanBW(false), m_offset(0, 0), m_subsampling(1) +{ +} + +void TRasterImage::setRaster(const TRasterP &raster) +{ + m_mainRaster = raster; + m_savebox = TRect(0, 0, raster->getLx() - 1, raster->getLy() - 1); +} + +//--------------------------------------------------------- + +TRasterImage::TRasterImage(const TRasterImage &src) + : m_mainRaster(src.m_mainRaster), m_patchRaster(src.m_patchRaster), m_iconRaster(src.m_iconRaster), m_dpix(src.m_dpix), m_dpiy(src.m_dpiy), m_name(src.m_name), m_savebox(src.m_savebox) + //, m_hPos(src.m_hPos) + , + m_isOpaque(src.m_isOpaque), m_isScanBW(src.m_isScanBW), m_offset(src.m_offset), m_subsampling(src.m_subsampling) +{ + if (m_mainRaster) + m_mainRaster = m_mainRaster->clone(); + if (m_patchRaster) + m_patchRaster = m_patchRaster->clone(); + if (m_iconRaster) + m_iconRaster = m_iconRaster->clone(); +} +//--------------------------------------------------------- + +TRasterImage::~TRasterImage() +{ +} + +//--------------------------------------------------------- + +TImage *TRasterImage::cloneImage() const +{ + return new TRasterImage(*this); +} + +//--------------------------------------------------------- + +void TRasterImage::setSubsampling(int s) +{ + m_subsampling = s; +} + +//--------------------------------------------------------- + +void TRasterImage::makeIcon(const TRaster32P &dstRas) +{ +#ifndef TNZCORE_LIGHT + assert(dstRas && dstRas->getLx() > 0 && dstRas->getLy() > 0); + + TRasterP &srcRas = m_mainRaster; + if (!srcRas || srcRas->getLx() <= 0 || srcRas->getLy() <= 0) { + dstRas->clear(); + return; + } + + double dpix = m_dpix, dpiy = m_dpiy; + if (dpix == 0) + dpix = 1; + if (dpiy == 0) + dpiy = 1; + double sx = (double)dstRas->getLx() * dpix / (double)srcRas->getLx(); + double sy = (double)dstRas->getLy() * dpiy / (double)srcRas->getLy(); + double sc = tmax(sx, sy); + TAffine aff = TScale(sc / dpix, sc / dpiy) + .place(srcRas->getCenterD(), dstRas->getCenterD()); + + TRop::resample(dstRas, srcRas, aff); +#endif +} + +//--------------------------------------------------------- diff --git a/toonz/sources/common/trop/bbox.cpp b/toonz/sources/common/trop/bbox.cpp new file mode 100644 index 0000000..c18dcdd --- /dev/null +++ b/toonz/sources/common/trop/bbox.cpp @@ -0,0 +1,228 @@ + + +#include "trop.h" + +namespace +{ + +//------------------------------------------------------------------- + +template +void computeBBox(TRasterPT ras, TRect &bbox) +{ + bbox = ras->getBounds(); + int lx = ras->getLx(); + int ly = ras->getLy(); + + // se c'e' un pixel opaco in alto a sin e in basso a destra allora bbox = bounds + if (ras->pixels(0)[0].m != 0 && + ras->pixels(ly - 1)[lx - 1].m != 0) + return; + + int y; + ras->lock(); + for (y = 0; y < ly; y++) { + CHANNEL_TYPE *pix = &(ras->pixels(y)->m); + CHANNEL_TYPE *endPix = pix + 4 * lx; + while (pix < endPix && *pix == 0) + pix += 4; + if (pix < endPix) + break; + } + if (y == ly) { + // tutta trasparente + bbox = TRect(); + ras->unlock(); + return; + } + bbox.y0 = y; + for (y = ly - 1; y > bbox.y0; y--) { + CHANNEL_TYPE *pix = &(ras->pixels(y)->m); + CHANNEL_TYPE *endPix = pix + 4 * lx; + while (pix < endPix && *pix == 0) + pix += 4; + if (pix < endPix) + break; + } + bbox.y1 = y; + assert(bbox.y0 <= bbox.y1); + bbox.x0 = lx; + bbox.x1 = -1; + for (y = bbox.y0; y <= bbox.y1; y++) { + CHANNEL_TYPE *row = &(ras->pixels(y)->m); + int x = 0; + for (x = 0; x < bbox.x0 && row[x * 4] == 0; x++) { + } + bbox.x0 = x; + for (x = lx - 1; x > bbox.x1 && row[x * 4] == 0; x--) { + } + bbox.x1 = x; + } + assert(bbox.x0 <= bbox.x1); + + ras->unlock(); + /* + + UCHAR *row_m = &(ras->pixels()->m); + // se c'e' un pixel opaco in alto a sin e in basso a destra allora bbox = bounds + if(row_m[0] && row_m[(ly-1)*wrap4 + (lx-1)*4]) + return; + + int y; + UCHAR *m, *max_m, *min_m; + + for(y=0;y=ly) + { + // tutta trasparente + return; + } + + bbox.y0 = bbox.y1 = y; + bbox.x0 = (m - row_m)/4; + assert(0<=bbox.x0 && bbox.x0pixels(bbox.y0)[bbox.x0].m>0); + assert(bbox.x0 == 0 || ras->pixels(bbox.y0)[bbox.x0-1].m==0); + + min_m = m; + for(m = max_m - 4;m>min_m && *m==0;m-=4) {} + bbox.x1 = (m - row_m)/4; + assert(0<=bbox.x1 && bbox.x1pixels(bbox.y0)[bbox.x1].m>0); + assert(bbox.x1 == lx-1 || ras->pixels(bbox.y0)[bbox.x1+1].m==0); + + row_m += wrap4; + + for(y++;ypixels(y)[bbox.x0].m>0); + assert(bbox.x0 == 0 || ras->pixels(y)[bbox.x0-1].m==0); + } + min_m = row_m + bbox.x1*4; + for(m = max_m - 4;m>min_m && *m==0;m-=4) {} + if(m>min_m) + { + x = (m - row_m)/4; + assert(x>bbox.x1); + bbox.x1 = x; + } + bbox.y1 = y; + } + row_m += wrap4; + } +*/ +} + +//------------------------------------------------------------------- + +inline bool isTransparent(TPixelCM32 *pix) +{ + return (!pix->isPureInk() && + // (pix->getPaint()==BackgroundStyle + (pix->getPaint() == 0 && pix->isPurePaint())); +} + +//------------------------------------------------------------------- + +void computeBBoxCM32(TRasterCM32P ras, TRect &bbox) +{ + bbox = ras->getBounds(); + int lx = ras->getLx(); + int ly = ras->getLy(); + + // se c'e' un pixel opaco in alto a sin e in basso a destra allora bbox = bounds + if (!isTransparent(&(ras->pixels(0)[0])) && + !isTransparent(&(ras->pixels(ly - 1)[lx - 1]))) + return; + + int y; + ras->lock(); + for (y = 0; y < ly; y++) { + TPixelCM32 *pix = ras->pixels(y); + TPixelCM32 *endPix = pix + lx; + while (pix < endPix && isTransparent(pix)) + ++pix; + if (pix < endPix) + break; + } + if (y == ly) { + // tutta trasparente + bbox = TRect(); + ras->unlock(); + return; + } + bbox.y0 = y; + for (y = ly - 1; y > bbox.y0; y--) { + TPixelCM32 *pix = ras->pixels(y); + TPixelCM32 *endPix = pix + lx; + while (pix < endPix && isTransparent(pix)) + ++pix; + if (pix < endPix) + break; + } + bbox.y1 = y; + assert(bbox.y0 <= bbox.y1); + bbox.x0 = lx; + bbox.x1 = -1; + for (y = bbox.y0; y <= bbox.y1; y++) { + TPixelCM32 *row = ras->pixels(y); + int x = 0; + for (x = 0; x < bbox.x0 && isTransparent(&row[x]); x++) { + } + bbox.x0 = x; + for (x = lx - 1; x > bbox.x1 && isTransparent(&row[x]); x--) { + } + bbox.x1 = x; + } + assert(bbox.x0 <= bbox.x1); + ras->unlock(); +} + +//------------------------------------------------------------------- + +} //namespace + +//------------------------------------------------------------------- + +void TRop::computeBBox(TRasterP ras, TRect &bbox) +{ + TRaster32P ras32 = ras; + if (ras32) { + ::computeBBox(ras32, bbox); + return; + } + + TRaster64P ras64 = ras; + if (ras64) { + ::computeBBox(ras64, bbox); + return; + } + + TRasterCM32P rasCM32 = ras; + if (rasCM32) { + computeBBoxCM32(rasCM32, bbox); + return; + } + + TRasterGR8P ras8 = ras; + if (ras8) { + bbox = ras->getBounds(); + return; + } + assert(0); +} diff --git a/toonz/sources/common/trop/borders_extractor.h b/toonz/sources/common/trop/borders_extractor.h new file mode 100644 index 0000000..7b8433a --- /dev/null +++ b/toonz/sources/common/trop/borders_extractor.h @@ -0,0 +1,97 @@ + + +#ifndef BORDERS_EXTRACTOR_H +#define BORDERS_EXTRACTOR_H + +#include "traster.h" +#include "runsmap.h" + +#include "raster_edge_iterator.h" + +namespace TRop +{ +namespace borders +{ + +//******************************************************************* +// Pixel Selector model +//******************************************************************* + +/*! + This class is just a stub for pixel selector classes. + + Pixel Selectors are objects used in raster borders recognition + to interpret a raster's pixels and direct edge iterators. +\n\n + The main purpose of this class is that of distinguishing pixels + from their values <\I>. Values are the actual entities recognized + in the borders extraction process - adjacent pixels that are cast to + the same value will be included in the same border. +\n\n + For example, if we want to extract the borders of a fullcolor image + that separate bright areas from dark ones, we can just have a pixel selector + cast each pixel to the integer value 0 if the pixel's value is below the + brigthness threshold, and 1 if above. +\n\n + Also, pixel selectors tell whether pixels are to be intended as + completely transparent (virtual pixels outside the raster boundaries are). +*/ +template +class pixel_selector +{ +public: + typedef Pix pixel_type; //!< The pixel type naming the selector + typedef Val value_type; //!< A value type representing pixel contents. + //!< Typically the pixel itself. + +public: + value_type value(const pixel_type &pix) const; + bool equal(const pixel_type &a, const pixel_type &b) const + { + return value(a) == value(b); + } + + value_type transparent() const; + bool transparent(const pixel_type &pix) const + { + return value(pix) == transparent(); + } + + //! Returns whether a border point must be read or not (corners are always read). + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return true; } +}; + +//---------------------------------------------------------------------------------- + +enum RunType { _BORDER_LEFT = 0x20, + _BORDER_RIGHT = 0x10, + _HIERARCHY_INCREASE = 0x8, + _HIERARCHY_DECREASE = 0x4 }; + +//---------------------------------------------------------------------------------- + +/*! + Reads the borders of the input raster, according to the specified selector. + Outputs the borders by supplying a container reader with the raster edge iterators + corresponding to border vertices. The runsmap used in the process can be + returned in output. +*/ +template +void readBorders(const TRasterPT &raster, const PixelSelector &selector, + ContainerReader &reader, RunsMapP *rasterRunsMap = 0); + +//===================================================================================== + +template +void readMeshes(const TRasterPT &raster, const PixelSelector &selector, + ContainersReader &meshesDataReader, RunsMapP *rasterRunsMap = 0); +} +} //namespace TRop::borders + +#endif //BORDERS_EXTRACTOR_H + +//===================================================================================== + +#ifdef INCLUDE_HPP +#include "borders_extractor.hpp" +#endif //INCLUDE_HPP diff --git a/toonz/sources/common/trop/borders_extractor.hpp b/toonz/sources/common/trop/borders_extractor.hpp new file mode 100644 index 0000000..aac7f70 --- /dev/null +++ b/toonz/sources/common/trop/borders_extractor.hpp @@ -0,0 +1,541 @@ + +#ifndef BORDERS_EXTRACTOR_HPP +#define BORDERS_EXTRACTOR_HPP + +//Toonz includes +#include "raster_edge_iterator.h" + +//tcg includes +#include "tcg/tcg_traits.h" +#include "tcg/tcg_containers_reader.h" +#include "tcg/tcg_hash.h" +#include "tcg/tcg_misc.h" + +#include "borders_extractor.h" + +namespace TRop +{ +namespace borders +{ + +//********************************************************************************************************* +// Private stuff +//********************************************************************************************************* + +template +class _DummyReader +{ +public: + void openContainer(const RasterEdgeIterator &) {} + void addElement(const RasterEdgeIterator &) {} + void closeContainer() {} +}; + +//********************************************************************************************************* +// Borders Extraction procedure +//********************************************************************************************************* + +inline void _signEdge(RunsMapP &runsMap, int x, int y0, int y1, UCHAR increasingSign, UCHAR decreasingSign) +{ + for (; y0 < y1; ++y0) + runsMap->runHeader(x, y0) |= increasingSign; + + if (y0 > y1) { + --x; + do { + --y0; + runsMap->runHeader(x, y0) |= decreasingSign; + } while (y0 > y1); + } +} + +//--------------------------------------------------------------------------------------------- + +template +void _readBorder(const TRasterPT &rin, const PixelSelector &selector, + RunsMapP &runsMap, int x, int y, bool counter, ContainerReader &reader) +{ + typedef typename PixelSelector::value_type value_type; + + UCHAR increasingSign = _BORDER_LEFT, decreasingSign = _BORDER_RIGHT; + if (!counter) + increasingSign |= _HIERARCHY_INCREASE, decreasingSign |= _HIERARCHY_DECREASE; + + //First, read the border entirely, while erasing the border from + //the runsMap. + RasterEdgeIterator it(rin, selector, TPoint(x, y), counter ? TPoint(1, 0) : TPoint(0, 1)); + //++it; //As we could be in the middle of a straight edge, increment to get a corner + + TPoint start(it.pos()), startDir(it.dir()); + reader.openContainer(it); + + TPoint oldPos(start); + for (++it; it.pos() != start || it.dir() != startDir; ++it) { + const TPoint &currPos(it.pos()); + reader.addElement(it); + + //Sign the corresponding (vertical) edge + _signEdge(runsMap, oldPos.x, oldPos.y, currPos.y, increasingSign, decreasingSign); + + oldPos = currPos; + } + + _signEdge(runsMap, oldPos.x, oldPos.y, it.pos().y, increasingSign, decreasingSign); + + reader.closeContainer(); +} + +//--------------------------------------------------------------------------------------------- + +template +void readBorders(const TRasterPT &rin, const PixelSelector &selector, + ContainerReader &reader, RunsMapP *rasterRunsMap) +{ + typedef TRasterPT RasterTypeP; + typedef _DummyReader DummyReader; + + //First, extract the run-length representation for rin + RunsMapP runsMap; + if (rasterRunsMap && *rasterRunsMap) { + //If it was supplied, use it + runsMap = *rasterRunsMap; + runsMap->lock(); + } else { + //In case, build it anew + runsMap = RunsMapP(rin->getLx(), rin->getLy()); + + runsMap->lock(); + buildRunsMap(runsMap, rin, selector); + } + + if (rasterRunsMap) + //Return the runsMap if requested + *rasterRunsMap = runsMap; + + //Build a fake reader for internal borders + DummyReader dummyReader; + + //Now, use it to extract borders - iterate through runs and, whenever + //one is found with opaque color (ie not transparent), extract its + //associated border. The border is erased internally after the read. + int lx = rin->getLx(), ly = rin->getLy(); + + int hierarchyLevel = 0; + + int x, y; + for (y = 0; y < ly; ++y) { + Pixel *lineStart = rin->pixels(y), *pix; + TPixelGR8 *runsStart = runsMap->pixels(y), *run; + + UCHAR nextHeader, prevHeader = 0; + for (x = 0, pix = lineStart, run = runsStart; x < lx;) { + nextHeader = run->value; + + if (hierarchyLevel) { + if (prevHeader & _BORDER_RIGHT) { + if (prevHeader & _HIERARCHY_DECREASE) + --hierarchyLevel; + } else //Every right border in a region should be signed. Do so now. + _readBorder(rin, selector, runsMap, x, y, true, dummyReader); + } + + if (hierarchyLevel) { + if (nextHeader & _BORDER_LEFT) { + if (nextHeader & _HIERARCHY_INCREASE) + ++hierarchyLevel; + } else { + ++hierarchyLevel; + _readBorder(rin, selector, runsMap, x, y, false, reader); + } + } else { + if (!(selector.transparent(*pix))) //External transparent region - do not extract + { + ++hierarchyLevel; + if (!(nextHeader & _BORDER_LEFT)) + _readBorder(rin, selector, runsMap, x, y, false, reader); + } + } + + //Increment variables + x += runsMap->runLength(x, y), pix = lineStart + x, run = runsStart + x; + prevHeader = (run - 1)->value; + } + + assert(x == lx); + + if (hierarchyLevel) { + assert((prevHeader & _BORDER_RIGHT) && (prevHeader & _HIERARCHY_DECREASE)); + --hierarchyLevel; + } + + assert(!hierarchyLevel); + } + + runsMap->unlock(); +} + +//********************************************************************************************************* +// New Mesh Extraction procedure +//********************************************************************************************************* + +enum { _PROCESSED = 0x1, + _HIERARCHY_UP = 0x2, + _HIERARCHY_DN = 0x4, + _PROCESSED_AND_HIERARCHY_UP = (_PROCESSED | _HIERARCHY_UP) }; + +//------------------------------------------------------------------- + +template +inline bool _isVertex(const RasterEdgeIter &it, const typename RasterEdgeIter::value_type &oldOtherColor) +{ + return (it.otherColor() != oldOtherColor) || + (it.turn() == it.adherence() && (!(it.turn() & RasterEdgeIter::AMBIGUOUS)) && + it.elbowColor() != oldOtherColor); +} + +//------------------------------------------------------------------- + +inline size_t _pointHash(const TPoint &point) { return point.x ^ point.y; } + +//------------------------------------------------------------------- + +template +struct _ExternalEdgeSigner { + static inline void signAndIncrement(RunsMapP &runsMap, RasterEdgeIter &it) + { + if (it.dir().y > 0) { + TPoint pos = it.pos(); + int newY = (++it).pos().y; + + for (; pos.y != newY; ++pos.y) + runsMap->runHeader(pos.x, pos.y) |= _PROCESSED_AND_HIERARCHY_UP; + } else if (it.dir().y < 0) { + TPoint pos = it.pos(); + int newY = (++it).pos().y; + + TPixelGR8 *pix = runsMap->pixels(pos.y - 1) + pos.x; + + for (; pos.y != newY; --pos.y, --pix) { + pix->value |= _PROCESSED; + (--pix)->value |= _HIERARCHY_DN; + } + } else + ++it; + } +}; + +//------------------------------------------------------------------- + +template +struct _InternalEdgeSigner { + static inline void signAndIncrement(RunsMapP &runsMap, RasterEdgeIter &it) + { + if (it.dir().y) { + TPoint pos = it.pos(); + int newY = (++it).pos().y; + int dir = it.dir().y; + + TPixelGR8 *pix = runsMap->pixels((it.dir().y > 0) ? pos.y : pos.y - 1) + pos.x; + + for (; pos.y != newY; pos.y += dir, pix += dir) { + pix->value |= _PROCESSED_AND_HIERARCHY_UP; + (--pix)->value |= _HIERARCHY_DN; + } + } else + ++it; + } +}; + +//------------------------------------------------------------------- + +template +int _readEdge(RasterEdgeIter &it, const RasterEdgeIter &end, RunsMapP runsMap, + int &vIdx, Mesh &mesh, tcg::hash &pointsHash, ContainersReader &reader) +{ + typedef tcg::container_reader_traits edge_output; + + typename Mesh::edge_type ed; + + ed.addVertex(vIdx); + ed.direction(0) = it.dir(); + + edge_output::openContainer(reader, it); + + typename RasterEdgeIter::value_type oldOtherColor = it.otherColor(); + do { + EdgeSigner::signAndIncrement(runsMap, it); + edge_output::addElement(reader, it); + + } while ((it != end) && !_isVertex(it, oldOtherColor)); + + // Identify the newly found vertex. If it's a brand new one, add it + tcg::hash::iterator ht = pointsHash.find(it.pos()); + vIdx = (ht == pointsHash.end()) ? pointsHash[it.pos()] = mesh.addVertex(typename Mesh::vertex_type(it.pos())) : ht.m_idx; + + ed.addVertex(vIdx); + ed.direction(1) = (it.turn() == RasterEdgeIter::STRAIGHT) ? -it.dir() : (it.turn() == RasterEdgeIter::LEFT) ? tcg::point_ops::ortLeft(it.dir()) : tcg::point_ops::ortRight(it.dir()); + + int eIdx = mesh.addEdge(ed); + edge_output::closeContainer(reader, &mesh, eIdx); + + return eIdx; +} + +//--------------------------------------------------------------------------------------------- + +template +void _readMeshes(const RasterEdgeIter &begin, RunsMapP &runsMap, ContainersReader &reader) +{ + typedef tcg::container_reader_traits face_output; + + // Iterate it clockwise. Process lines with vertical displacement. In each line, search + // for unprocessed raster edges. + + // Use hierarchy signs in the runsMap to understand the search scope in this sub-region. + int hierarchyLevel = 0; + + RasterEdgeIter it(begin); + do { + if (it.dir().y > 0) { + // Process line + TPoint pos = it.pos(); + const TPixelGR8 *pix = runsMap->pixels(pos.y) + pos.x; + + hierarchyLevel = 0; + assert((pix->value & _PROCESSED) && (pix->value & _HIERARCHY_UP)); + + do { + // Iterate through the line. Extract a mesh each time an unprocessed raster edge is found. + if (!(pix->value & _PROCESSED)) { + assert(hierarchyLevel == 1); + + Mesh *meshPtr = new Mesh; + _readMesh(it.raster(), it.selector(), runsMap, pos.x, pos.y, *meshPtr, reader); + + face_output::addElement(reader, meshPtr); + } + + if (pix->value & _HIERARCHY_UP) + ++hierarchyLevel; + + TUINT32 l = runsMap->runLength(pos.x, pos.y); + pos.x += l; + pix += l; + + if ((pix - 1)->value & _HIERARCHY_DN) + --hierarchyLevel; + + } while (hierarchyLevel > 0); + } + + ++it; + + } while (it != begin); +} + +//------------------------------------------------------------------- + +template +void _readBorder(const RasterEdgeIter &begin, RunsMapP runsMap, + int vIdx, Mesh &mesh, tcg::hash &pointsHash, + ContainersReader &reader) +{ + typedef typename Mesh::face_type face_type; + typedef typename Mesh::edge_type edge_type; + typedef tcg::container_reader_traits face_output; + + // As long as we don't get back to the initial iterator, extract edges + RasterEdgeIter it(begin); + + // Create the face to be extracted at the right of processed border, and add it to + // the mesh. Observe that insertion is made manually in the mesh's faces list. + // This prevents the mesh from automatically link edges to the face. + face_type fc; + + do { + // Retrieve current vertex + typename Mesh::vertex_type &vx = mesh.vertex(vIdx); + + // Search in it the edge corresponding to current iterating direction + int e, edgesCount = vx.edgesCount(), eIdx = -1, side = -1; + for (e = 0; e < edgesCount; ++e) { + edge_type &ed = mesh.edge(vx.edge(e)); + side = ed.vertex(0) == vIdx ? 0 : 1; + + if (ed.direction(side) == it.dir()) { + eIdx = ed.getIndex(); + break; + } + } + + if (e == edgesCount) { + // In case the edge was not found, it needs to be extracted now. + eIdx = _readEdge>( + it, begin, runsMap, vIdx, mesh, pointsHash, reader); + } else { + // The edge was already extracted. We just need to update the iterator then. + const edge_type &ed = mesh.edge(eIdx); + + vIdx = ed.vertex(1 - side); + const TPoint &oppositePos = mesh.vertex(vIdx).P(); + const TPoint &oppositeDir = ed.direction(1 - side); + + // We need to perform the last ++it in the already extracted edge since we need + // to give it the incoming direction. + it.setEdge(oppositePos + oppositeDir, -oppositeDir), ++it; + } + + fc.addEdge(eIdx); + + } while (it != begin); + + // The face has now been described (from the mesh viewpoint). Add it to the mesh. + int fIdx = mesh.addFace(fc); + + // We still need to extract its sub-meshes content now. + face_output::openContainer(reader, &mesh, fIdx, begin.rightColor()); + + _readMeshes(begin, runsMap, reader); + + face_output::closeContainer(reader); +} + +//------------------------------------------------------------------- + +template +void _readMesh(const TRasterPT &rin, + const PixelSelector &selector, RunsMapP &runsMap, int x, int y, + Mesh &mesh, ContainersReader &reader) +{ + typedef typename Mesh::vertex_type vertex_type; + typedef typename Mesh::edge_type edge_type; + typedef tcg::container_reader_traits edge_output; + typedef typename PixelSelector::value_type value_type; + typedef RasterEdgeIterator raster_edge_iterator; + + // Iterate silently until a vertex is encountered (or until the initial point is found) + raster_edge_iterator it(rin, selector, TPoint(x, y), TPoint(0, 1)), begin(it); + it.setAdherence(raster_edge_iterator::LEFT); + + value_type beginColor = begin.rightColor(); + for (++it; it != begin && !_isVertex(it, beginColor); ++it) + ; + + // Use a hash to keep track of found vertices + tcg::hash pointsHash(&_pointHash); + + int vIdx = pointsHash[it.pos()] = mesh.addVertex(it.pos()); + + // The outer edges are extracted first in clockwise orientation. + begin = it; + do { + _readEdge>( + it, begin, runsMap, vIdx, mesh, pointsHash, reader); + + } while (it != begin); + + // Then, their associated faces are extracted. + it.setAdherence(raster_edge_iterator::RIGHT); + + int e, outerEdgesCount = mesh.edgesCount(); + for (e = 0; e < outerEdgesCount; ++e) { + const edge_type &ed = mesh.edge(e); + if (ed.face(0) < 0) { + vIdx = ed.vertex(0); + const vertex_type &vx = mesh.vertex(vIdx); + + it.setEdge(vx.P(), ed.direction(0)); + _readBorder(it, runsMap, vIdx, mesh, pointsHash, reader); + } + } + + // Edges following those must have either side associated with a face. + // Which must be extracted too. + for (e = outerEdgesCount; e < mesh.edgesCount(); ++e) { + const edge_type &ed = mesh.edge(e); + if (ed.face(1) < 0) { + vIdx = ed.vertex(1); + const vertex_type &vx = mesh.vertex(vIdx); + + it.setEdge(vx.P(), ed.direction(1)); + _readBorder(it, runsMap, vIdx, mesh, pointsHash, reader); + } + } +} + +//--------------------------------------------------------------------------------------------- + +template +void readMeshes(const TRasterPT &rin, const PixelSelector &selector, + ContainersReader &reader, RunsMapP *rasterRunsMap) +{ + typedef typename PixelSelector::pixel_type pixel_type; + typedef TRasterPT RasterTypeP; + typedef Mesh mesh_type; + typedef tcg::container_reader_traits face_output; + + // First, extract the run-length representation for rin + RunsMapP runsMap; + if (rasterRunsMap && *rasterRunsMap) { + // If a runsmap was supplied, use it + runsMap = *rasterRunsMap; + runsMap->lock(); + + assert((runsMap->getLx() == rin->getLx() + 1) && (runsMap->getLy() == rin->getLy())); + } else { + // In case, build it anew + runsMap = RunsMapP(rin->getLx() + 1, rin->getLy()); + + // Observe the +1 on the x-axis. One additional pixel is currently required on the right + // side of the runsmap - for ease of use in the algorithm. + + runsMap->lock(); + buildRunsMap(runsMap, rin, selector); + } + + if (rasterRunsMap) + // Return the runsMap if requested + *rasterRunsMap = runsMap; + + face_output::openContainer(reader, 0, -1, selector.transparent()); + + // Now, use it to extract borders - iterate through runs and, whenever + // one is found with opaque color (ie not transparent), extract its + // associated border. The border is erased internally after the read. + int lx = rin->getLx(), ly = rin->getLy(); + + int x, y; + for (y = 0; y < ly; ++y) { + // Process each row + + pixel_type *lineStart = rin->pixels(y), *pix; + TPixelGR8 *runsStart = runsMap->pixels(y), *run; + + UCHAR nextHeader, prevHeader = 0; + for (x = 0, pix = lineStart, run = runsStart; x < lx;) { + nextHeader = run->value; + + if (!(selector.transparent(*pix) || nextHeader & _PROCESSED)) { + mesh_type *meshPtr = new mesh_type; + + // Read the mesh. All its internal sub-meshes are read as well. + _readMesh(rin, selector, runsMap, x, y, *meshPtr, reader); + face_output::addElement(reader, meshPtr); + } + + //Increment variables + x += runsMap->runLength(x, y), pix = lineStart + x, run = runsStart + x; + prevHeader = (run - 1)->value; + } + + assert(x == lx); + } + + face_output::closeContainer(reader); + + runsMap->unlock(); +} +} +} //namespace TRop::borders + +#endif //BORDERS_EXTRACTOR_HPP diff --git a/toonz/sources/common/trop/brush.cpp b/toonz/sources/common/trop/brush.cpp new file mode 100644 index 0000000..e83f093 --- /dev/null +++ b/toonz/sources/common/trop/brush.cpp @@ -0,0 +1,623 @@ + + +#include "trop.h" + +//======================================================================= + +class HalfCord +{ + int *m_array; + int m_radius; + +public: + HalfCord(int radius) + { + assert(radius >= 0); + m_radius = radius; + m_array = new int[m_radius + 1]; + memset(m_array, 0, (m_radius + 1) * sizeof(int)); + + float dCircle = 1.25f - m_radius; // inizializza decision variable + int y = m_radius; // inizializzazione indice scanline + int x = 0; // inizializzazione indice colonna + do { + m_array[y] = tmax(x, m_array[y]); + m_array[x] = y; + if (dCircle <= 0) { + dCircle = dCircle + 2 * x + 3; + } else { + y--; + dCircle = dCircle + 2 * (x - y) + 5; + } + x++; + + } while (y >= x); + } + + ~HalfCord() + { + delete[] m_array; + } + inline int getCord(int x) + { + assert(0 <= x && x <= m_radius); + return m_array[x]; + }; + +private: + // not implemented + HalfCord(const HalfCord &); + HalfCord &operator=(const HalfCord &); +}; + +//======================================================================= + +void TRop::brush( + TRaster32P ras, + const TPoint &aa, + const TPoint &bb, + int radius, + const TPixel32 &col) +{ + + TPoint a = aa; + TPoint b = bb; + if (a.y > b.y) + tswap(a, b); // a e' piu' in basso di b + + int lx = ras->getLx(); + int ly = ras->getLy(); + ras->lock(); + + // ----- radius = 0 + if (radius == 0) { + // k = +1/-1 se il rettangolo e' inclinato positivamente (0<=m)/negativamente (m<0) + // (se k<0 viene fatta una riflessione sulle ascisse prima di tornare alle + // coordinate "di schermo") + int k = 1; + int dy = b.y - a.y; + int dx = b.x - a.x; + if (dx < 0) { + dx = -dx; + k = -1; + } + + assert(dx >= 0); + assert(dy >= 0); + + double m; // m sara' definita solo per dx!=0) + if (dx > 0) { + m = dy / (double)dx; + } + //double length = sqrt(dx*dx + dy*dy); + const int alpha = dy, beta = -dx; + const int incE = alpha; + const int incNE = alpha + beta; + const int incN = beta; + + // N.B. le coordinate sono relative ad un sist. di rif. con l'origine in a + // l'eq. della retta e' alpha * x + beta * y = 0 + + int yMin = tmax(a.y, 0) - a.y; // clipping y + cambio riferimento + int yMax = tmin(b.y, ly - 1) - a.y; // (trasporto dell'origine in a) + if (dx > 0 && m <= 1) { + // midpoint algorithm + TPoint segm; + if (dy == 0) // segmento orizzontale: inizializza segm + { + segm.x = 0; + segm.y = yMin; + } else // 0 0) { + xMin = tmax(a.x + segm.x - count, a.x, 0); // clipping x + ritorno alle + xMax = tmin(a.x + segm.x, b.x, lx - 1); // coordinate "di schermo" + + } else { + xMin = tmax(a.x - segm.x, a.x - dx, 0); // clipping x + riflessione + ritorno + xMax = tmin(a.x - segm.x + count, a.x, lx - 1); // alle coordinate "di schermo" + } + + TPixel32 *p = ras->pixels(segm.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + + dSegm = dSegm + incNE; + segm.x++; + segm.y++; + } + } else // m>1 oppure segmento verticale + { + // midpoint algorithm + TPoint segm; + if (dx == 0) // segmento verticale: inizializza segm + { + segm.x = 0; + segm.y = yMin; + } else // m>1 : inizializza segm + { + segm.x = tround(yMin / m); + segm.y = yMin; + } + + int dSegm = tfloor(alpha * (segm.x + 0.5) + beta * (segm.y + 1)); + while (segm.y <= yMax) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x + segm.x, 0); // clipping x + ritorno alle + xMax = tmin(a.x + segm.x, lx - 1); // coordinate "di schermo" + + } else { + xMin = tmax(a.x - segm.x, 0); // clipping x + riflessione + ritorno + xMax = tmin(a.x - segm.x, lx - 1); // alle coordinate "di schermo" + } + + TPixel32 *p = ras->pixels(segm.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + + if (dSegm <= 0) // NordEst + { + dSegm = dSegm + incNE; + segm.x++; + } else // Nord + { + dSegm = dSegm + incN; + } + segm.y++; + } + } + ras->unlock(); + return; + } + + HalfCord halfCord(radius); + + int x, y; + + // ----- punti iniziali coincidenti: disegna un cerchio + if (a == b) { + int yMin = tmax(a.y - radius, 0); // clipping y + int yMax = tmin(a.y + radius, ly - 1); // clipping y + for (y = yMin; y <= yMax; y++) { + int deltay = abs(y - a.y); + int xMin = tmax(a.x - halfCord.getCord(deltay), 0); // clipping x + int xMax = tmin(a.x + halfCord.getCord(deltay), lx - 1); // clipping x + TPixel32 *p = ras->pixels(y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + } + ras->unlock(); + return; + } + + // ----- rettangolo orizzontale (a.y = b.y, a.x != b.x) + if (a.y == b.y) { + int yMin = tmax((a.y - radius), 0); // clipping y + int yMax = tmin((a.y + radius), ly - 1); // clipping y + int xLeft = tmin(a.x, b.x); + int xRight = tmax(a.x, b.x); + for (y = yMin; y <= yMax; y++) { + int deltay = abs(y - a.y); + int xMin = tmax(xLeft - halfCord.getCord(deltay), 0); // clipping x + int xMax = tmin(xRight + halfCord.getCord(deltay), lx - 1); // clipping x + TPixel32 *p = ras->pixels(y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + } + ras->unlock(); + return; + } + + // ----- rettangolo verticale (a.x = b.x, a.y != b.y) + if (a.x == b.x) { + + int xMin = tmax(a.x - radius, 0); // clipping x + int xMax = tmin(a.x + radius, lx - 1); // clipping x + for (x = xMin; x <= xMax; x++) { + int deltax = abs(x - a.x); + int yMin = tmax(a.y - halfCord.getCord(deltax), 0); // clipping y + int yMax = tmin(b.y + halfCord.getCord(deltax), ly - 1); // clipping y + if (yMin <= yMax) { + TPixel32 *p = ras->pixels(yMin) + x; + TPixel32 *q = ras->pixels(yMax) + x; + int wrap = ras->getWrap(); + while (p <= q) { + *p = col; + p += wrap; + } + } + } + ras->unlock(); + return; + } + + // ----- rettangolo inclinato + // k = +1/-1 se il rettangolo e' inclinato positivamente/negativamente + int k = 1; + int dx = b.x - a.x; + if (dx < 0) { + dx = -dx; + k = -1; + } + int dy = b.y - a.y; + + assert(dx > 0); + assert(dy > 0); + + double length = sqrt((double)(dx * dx + dy * dy)); + const double m = dy / (double)dx; + + //punto di tangenza superiore nel sistema di riferimento del cerchio + TPointD up(-radius * dy / length, radius * dx / length); + + //semi-ampiezza orizzontale delle "calotte" circolari + int halfAmplCap = tfloor(-up.x); + + // A meno di intersezioni relative tra le diverse zone: + + // le scanline della "calotta" circolare superiore sono (b.y+cutExt,b.y+radius] + // le scanline del trapezoide circolare superiore sono [b.y-cutIn,b.y+cutExt] + // le scanline del parallelogramma sono (a.y+cutIn,b.y-cutIn) + // le scanline del trapezoide circolare inferiore sono [a.y-cutExt,a.y+cutIn] + // le scanline della "calotta" circolare inferiore sono [a.y-radius,a.y-cutExt) + int cutExt, cutIn; + + // vertici del parallelogramma + TPointD rightUp; + TPointD rightDown; + TPointD leftUp; + TPointD leftDown; + double mParall; //coeff. angolare parallelogramma + + // NOTA BENE: halfAmplCap=0 <=> (radius=0 (caso a parte) , 1) + if (radius > 1) { + for (cutExt = radius; cutExt >= 0 && halfCord.getCord(cutExt) <= halfAmplCap; cutExt--) + ; + cutIn = cutExt; // vedi else successivo + rightUp.x = dx + halfCord.getCord(cutIn); + rightUp.y = dy - cutIn; + rightDown.x = halfCord.getCord(cutIn); + rightDown.y = -cutIn; + leftUp.x = dx - halfCord.getCord(cutIn); + leftUp.y = dy + cutIn; + leftDown.x = -halfCord.getCord(cutIn); + leftDown.y = cutIn; + mParall = dy / (double)dx; + } else // N.B. cutExt != cutIn solo quando radius=1 + { + cutExt = radius; // radius=1 => halfAmplCap=0 (non ci sono mai le "calotte" circolari) + cutIn = 0; // anche per radius=1 il limite "interno" dei trapezoidi circolari e' < radius + rightUp.x = dx - up.x; + rightUp.y = dy - up.y; + rightDown.x = -up.x; + rightDown.y = -up.y; + leftUp.x = dx + up.x; + leftUp.y = dy + up.y; + leftDown.x = up.x; + leftDown.y = up.y; + mParall = m; + } + // ----- riempie "calotte" circolari + + // ----- riempie "calotta" circolare inferiore + int yMin = tmax(a.y - radius, 0); // clipping y + int yMax = tmin(a.y - cutExt - 1, ly - 1); // clipping y + for (y = yMin; y <= yMax; y++) { + int r = halfCord.getCord(a.y - y); + int xMin = tmax(a.x - r, 0); // clipping x + int xMax = tmin(a.x + r, lx - 1); // clipping x + TPixel32 *p = ras->pixels(y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + } + // ----- riempie "calotta" circolare superiore + yMin = tmax(b.y + cutExt + 1, 0); // clipping y + yMax = tmin(b.y + radius, ly - 1); // clipping y + for (y = yMin; y <= yMax; y++) { + int r = halfCord.getCord(y - b.y); + int xMin = tmax(b.x - r, 0); // clipping x + int xMax = tmin(b.x + r, lx - 1); // clipping x + TPixel32 *p = ras->pixels(y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + } + // ----- riempie trapezoidi + + // (se k<0 viene fatta una riflessione sulle ascisse prima di tornare alle + // coordinate "di schermo") + + // limite destro assoluto delle scanline trapezoide: + int xSegmMax = tround(dx - up.x); // coordinata x del punto di tangenza inferiore sul cerchio superiore + + // limite sinistro assoluto delle scanline: + int xSegmMin = tround(up.x); // coordinata x del punto di tangenza superiore sul cerchio inferiore + + // ----- riempie trapezoide inferiore + + // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro + // del cerchio inferiore + + yMin = tmax(a.y - cutExt, 0) - a.y; // clipping y + yMax = tmin(a.y + cutIn, b.y - cutIn - 1, ly - 1) - a.y; // clipping y + + // l'eq. della retta e' alpha * x + beta * y + gammaRight = 0 + const int alpha = dy, beta = -dx; + const double gammaRight = rightDown.y * dx - rightDown.x * dy; + const int incE = alpha; + const int incNE = alpha + beta; + const int incN = beta; + + if (m <= 1) { + // midpoint algorithm; le scanline vengono disegnate solo + // sul NordEst. L'ultima scanline non viene disegnata + TPoint segmRight(tceil((yMin + 0.5 - rightDown.y) / mParall + rightDown.x) - 1, yMin); + int dSegmRight = tfloor(alpha * (segmRight.x + 1) + beta * (segmRight.y + 0.5) + gammaRight); + while (segmRight.y <= yMax) { + if (dSegmRight < 0) // Est + { + dSegmRight = dSegmRight + incE; + segmRight.x++; + } else // NordEst + { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x - halfCord.getCord(abs(segmRight.y)), 0); // clipping x + xMax = tmin(a.x + tmin(segmRight.x, xSegmMax), lx - 1); // clipping x + } else { + xMin = tmax(a.x - tmin(segmRight.x, xSegmMax), 0); // clipping x + ritorno alle + xMax = tmin(a.x + halfCord.getCord(abs(segmRight.y)), lx - 1); // coordinate "di schermo" + } + TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + + dSegmRight = dSegmRight + incNE; + segmRight.x++; + segmRight.y++; + } + } + } else // m>1 + { + // midpoint algorithm; le scanline vengono disegnate sempre + TPoint segmRight(tround((yMin - rightDown.y) / mParall + rightDown.x), yMin); + int dSegmRight = tfloor(alpha * (segmRight.x + 0.5) + beta * (segmRight.y + 1) + gammaRight); + while (segmRight.y <= yMax) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x - halfCord.getCord(abs(segmRight.y)), 0); // clipping x + xMax = tmin(a.x + segmRight.x, lx - 1); // clipping x + } else { + xMin = tmax(a.x - segmRight.x, 0); // clipping x + ritorno alle coordinate + xMax = tmin(a.x + halfCord.getCord(abs(segmRight.y)), lx - 1); // "di schermo" + } + TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + + if (dSegmRight <= 0) // NordEst + { + dSegmRight = dSegmRight + incNE; + segmRight.x++; + } else // Nord + { + dSegmRight = dSegmRight + incN; + } + segmRight.y++; + } + } + + // ----- riempie trapezoide superiore + + // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro + // del cerchio superiore + yMin = tmax(b.y - cutIn, a.y + cutIn + 1, 0) - b.y; // clipping y + yMax = tmin(b.y + cutExt, ly - 1) - b.y; // clipping y + + // l'eq. della retta e' alpha * x + beta * y + gammaLeft = 0 + const double gammaLeft = leftDown.y * dx - leftDown.x * dy; + + if (m <= 1) { + // midpoint algorithm; le scanline vengono disegnate solo + // sul NordEst. L'ultima scanline non viene disegnata + TPoint segmLeft(tceil((yMin - 0.5 - leftDown.y) / mParall + leftDown.x), yMin); + int dSegmLeft = tfloor(alpha * (segmLeft.x + 1) + beta * (segmLeft.y + 0.5) + gammaLeft); + while (segmLeft.y <= yMax) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(b.x + tmax(segmLeft.x, xSegmMin - dx), 0); // clipping x + xMax = tmin(b.x + halfCord.getCord(abs(segmLeft.y)), lx - 1); // clipping x + } else { + xMin = tmax(b.x - halfCord.getCord(abs(segmLeft.y)), 0); // clipping x + ritorno alle + xMax = tmin(b.x - tmax(segmLeft.x, xSegmMin - dx), lx - 1); // coordinate "di schermo" + } + TPixel32 *p = ras->pixels(segmLeft.y + b.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + while (dSegmLeft < 0) { + dSegmLeft = dSegmLeft + incE; + segmLeft.x++; + } + dSegmLeft = dSegmLeft + incNE; + segmLeft.x++; + segmLeft.y++; + } + } else // m>1 + { + // midpoint algorithm; le scanline vengono disegnate sempre + TPoint segmLeft(tround((yMin - leftDown.y) / mParall + leftDown.x), yMin); + int dSegmLeft = tfloor(alpha * (segmLeft.x + 0.5) + beta * (segmLeft.y + 1) + gammaLeft); + while (segmLeft.y <= yMax) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(b.x + segmLeft.x, 0); // clipping x + xMax = tmin(b.x + halfCord.getCord(abs(segmLeft.y)), lx - 1); // clipping x + } else { + xMin = tmax(b.x - halfCord.getCord(abs(segmLeft.y)), 0); // clipping x + ritorno alle + xMax = tmin(b.x - segmLeft.x, lx - 1); // coordinate "di schermo" + } + TPixel32 *p = ras->pixels(segmLeft.y + b.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + + if (dSegmLeft <= 0) // NordEst + { + dSegmLeft = dSegmLeft + incNE; + segmLeft.x++; + } else // Nord + { + dSegmLeft = dSegmLeft + incN; + } + segmLeft.y++; + } + } + + // ----- parallelogramma (in alternativa a "parallelogrammoide circolare") + + // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro + // del cerchio inferiore + + // retta destra di equaz. alpha * x + beta * y + gammaRight = 0 + // retta sinistra di equaz. alpha * x + beta * y + gammaLeft = 0 + + yMin = tmax(a.y + cutIn + 1, 0) - a.y; //clipping y + yMax = tmin(b.y - cutIn - 1, ly - 1) - a.y; //clipping y + if (m <= 1) { + // midpoint algorithm; le scanline vengono disegnate solo + // sul NordEst. L'ultima scanline non viene disegnata + TPoint segmRight(tceil((yMin + 0.5 - rightDown.y) / mParall + rightDown.x) - 1, yMin); + TPoint segmLeft = TPoint(tceil((yMin - 0.5 - leftDown.y) / mParall + leftDown.x), yMin); + int dSegmRight = tfloor(alpha * (segmRight.x + 1) + beta * (segmRight.y + 0.5) + gammaRight); + int dSegmLeft = tfloor(alpha * (segmLeft.x + 1) + beta * (segmLeft.y + 0.5) + gammaLeft); + while (segmRight.y <= yMax) { + if (dSegmRight < 0) // segmRight a Est + { + dSegmRight = dSegmRight + incE; + segmRight.x++; + } else // segmRight a NordEst + { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x + tmax(segmLeft.x, xSegmMin), 0); // clipping x + xMax = tmin(a.x + tmin(segmRight.x, xSegmMax), lx - 1); // clipping x + } else { + xMin = tmax(a.x - tmin(segmRight.x, xSegmMax), 0); // clipping x + ritorno alle + xMax = tmin(a.x - tmax(segmLeft.x, xSegmMin), lx - 1); // coordinate "di schermo" + } + + TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + + dSegmRight = dSegmRight + incNE; + segmRight.x++; + segmRight.y++; + + while (dSegmLeft < 0) // segmLeft a Est + { + dSegmLeft = dSegmLeft + incE; + segmLeft.x++; + } + // segmLeft a NordEst + dSegmLeft = dSegmLeft + incNE; + segmLeft.x++; + segmLeft.y++; + } + } + } else // m>1 + { + // midpoint algorithm; le scanline vengono disegnate sempre + TPoint segmRight(tround((yMin - rightDown.y) / mParall + rightDown.x), yMin); + TPoint segmLeft(tround((yMin - leftDown.y) / mParall + leftDown.x), yMin); + int dSegmRight = tfloor(alpha * (segmRight.x + 0.5) + beta * (segmRight.y + 1) + gammaRight); + int dSegmLeft = tfloor(alpha * (segmLeft.x + 0.5) + beta * (segmLeft.y + 1) + gammaLeft); + while (segmRight.y <= yMax) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x + segmLeft.x, 0); // clipping x + xMax = tmin(a.x + segmRight.x, lx - 1); // clipping x + } else { + xMin = tmax(a.x - segmRight.x, 0); // clipping x + ritorno alle + xMax = tmin(a.x - segmLeft.x, lx - 1); // coordinate "di schermo" + } + + TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; + TPixel32 *q = p + (xMax - xMin); + + while (p <= q) + *p++ = col; + + if (dSegmRight <= 0) // segmRight a NordEst + { + dSegmRight = dSegmRight + incNE; + segmRight.x++; + } else // segmRight a Nord + { + dSegmRight = dSegmRight + incN; + } + segmRight.y++; + + if (dSegmLeft <= 0) // segmLeft a NordEst + { + dSegmLeft = dSegmLeft + incNE; + segmLeft.x++; + } else // segmLeft a Nord + { + dSegmLeft = dSegmLeft + incN; + } + } + } + + // ---- parallelogrammoide circolare (in alternativa a parallelogramma) + + // N.B. coordinate di schermo (riflessione per k<0 ) + + yMin = tmax(b.y - cutIn, 0); + yMax = tmin(a.y + cutIn, ly - 1); + for (y = yMin; y <= yMax; y++) { + int xMin, xMax; + if (k > 0) { + xMin = tmax(a.x - halfCord.getCord(abs(y - a.y)), 0); // clipping x + xMax = tmin(b.x + halfCord.getCord(abs(b.y - y)), lx - 1); // clipping x + } else { + xMin = tmax(b.x - halfCord.getCord(abs(b.y - y)), 0); // clipping x + ritorno alle + xMax = tmin(a.x + halfCord.getCord(abs(y - a.y)), lx - 1); // coordinate "di schermo" + } + TPixel32 *p = ras->pixels(y) + xMin; + TPixel32 *q = p + (xMax - xMin); + while (p <= q) + *p++ = col; + } + ras->unlock(); +} diff --git a/toonz/sources/common/trop/loop_macros.h b/toonz/sources/common/trop/loop_macros.h new file mode 100644 index 0000000..3b21ef8 --- /dev/null +++ b/toonz/sources/common/trop/loop_macros.h @@ -0,0 +1,336 @@ + + +#ifndef LOOP_MACROS_INCLUDED +#define LOOP_MACROS_INCLUDED + +/* + assert ((0 <= xI) && (xI <= up->getLx() - 1) && \ + (0 <= yI) && (yI <= up->getLy() - 1) ); \ +*/ + +#define INTERNAL_LOOP_THE_FIRST \ + xL += deltaXL; \ + yL += deltaYL; \ + xI = xL >> PADN; \ + yI = yL >> PADN; \ + upPix00 = upBasePix + (yI * upWrap + xI); \ + upPix10 = upPix00 + 1; \ + upPix01 = upPix00 + upWrap; \ + upPix11 = upPix00 + upWrap + 1; \ + xWeight1 = (xL & MASKN); \ + xWeight0 = (1 << PADN) - xWeight1; \ + yWeight1 = (yL & MASKN); \ + yWeight0 = (1 << PADN) - yWeight1; \ + c1 = MULT_2_X_16BIT(upPix00->r, upPix00->g, xWeight0); \ + c3 = MULT_2_X_16BIT(upPix00->b, upPix01->r, xWeight0); \ + c5 = MULT_2_X_16BIT(upPix01->g, upPix01->b, xWeight0); \ + c2 = MULT_2_X_16BIT(upPix10->r, upPix10->g, xWeight1); \ + c4 = MULT_2_X_16BIT(upPix10->b, upPix11->r, xWeight1); \ + c6 = MULT_2_X_16BIT(upPix11->g, upPix11->b, xWeight1); \ + s_gb = (c5 + c6) >> 16; \ + gColUpTmp = s_gb >> 32; \ + bColUpTmp = s_gb & 0x1FF; \ + s_br = (c3 + c4) >> 16; \ + bColDownTmp = s_br >> 32; \ + rColUpTmp = s_br & 0x1FF; \ + s_rg = (c1 + c2) >> 16; \ + rColDownTmp = s_rg >> 32; \ + gColDownTmp = s_rg & 0x1FF; \ + rCol = \ + (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); \ + gCol = \ + (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); \ + bCol = \ + (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); \ + *dnPix = TPixel32(rCol, gCol, bCol, upPix00->m); + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_FIRST_X_2 \ + INTERNAL_LOOP_THE_FIRST \ + ++dnPix; \ + INTERNAL_LOOP_THE_FIRST + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_FIRST_X_4 \ + INTERNAL_LOOP_THE_FIRST_X_2 \ + ++dnPix; \ + INTERNAL_LOOP_THE_FIRST_X_2 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_FIRST_X_8 \ + INTERNAL_LOOP_THE_FIRST_X_4 \ + ++dnPix; \ + INTERNAL_LOOP_THE_FIRST_X_4 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_FIRST_X_16 \ + INTERNAL_LOOP_THE_FIRST_X_8 \ + ++dnPix; \ + INTERNAL_LOOP_THE_FIRST_X_8 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_FIRST_X_32 \ + INTERNAL_LOOP_THE_FIRST_X_16 \ + ++dnPix; \ + INTERNAL_LOOP_THE_FIRST_X_16 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST \ + a = invAff * TPointD(xMin, y); \ + xL0 = tround(a.x * (1 << PADN)); \ + yL0 = tround(a.y * (1 << PADN)); \ + kMinX = 0; \ + kMaxX = xMax - xMin; \ + kMinY = 0; \ + kMaxY = xMax - xMin; \ + if (deltaXL == 0) { \ + if ((xL0 < 0) || (lxPred < xL0)) \ + continue; \ + } else if (deltaXL > 0) { \ + if (lxPred < xL0) \ + continue; \ + kMaxX = (lxPred - xL0) / deltaXL; \ + if (xL0 < 0) { \ + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; \ + } \ + } else { \ + if (xL0 < 0) \ + continue; \ + kMaxX = xL0 / (-deltaXL); \ + if (lxPred < xL0) { \ + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); \ + } \ + } \ + if (deltaYL == 0) { \ + if ((yL0 < 0) || (lyPred < yL0)) \ + continue; \ + } else if (deltaYL > 0) { \ + if (lyPred < yL0) \ + continue; \ + kMaxY = (lyPred - yL0) / deltaYL; \ + if (yL0 < 0) { \ + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; \ + } \ + } else { \ + if (yL0 < 0) \ + continue; \ + kMaxY = yL0 / (-deltaYL); \ + if (lyPred < yL0) { \ + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); \ + } \ + } \ + kMin = tmax(kMinX, kMinY, (int)0); \ + kMax = tmin(kMaxX, kMaxY, xMax - xMin); \ + dnPix = dnRow + xMin + kMin; \ + dnEndPix = dnRow + xMin + kMax + 1; \ + xL = xL0 + (kMin - 1) * deltaXL; \ + yL = yL0 + (kMin - 1) * deltaYL; \ + for (; dnPix < dnEndPix - 32; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST_X_32; \ + } \ + for (; dnPix < dnEndPix - 16; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST_X_16; \ + } \ + for (; dnPix < dnEndPix - 8; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST_X_8; \ + } \ + for (; dnPix < dnEndPix - 4; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST_X_4; \ + } \ + for (; dnPix < dnEndPix - 2; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST_X_2; \ + } \ + for (; dnPix < dnEndPix; ++dnPix) { \ + INTERNAL_LOOP_THE_FIRST \ + } + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST_X_2 \ + EXTERNAL_LOOP_THE_FIRST \ + ++y; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_FIRST + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST_X_4 \ + EXTERNAL_LOOP_THE_FIRST_X_2 \ + ++y; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_FIRST_X_2 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST_X_8 \ + EXTERNAL_LOOP_THE_FIRST_X_4 \ + ++y; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_FIRST_X_4 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST_X_16 \ + EXTERNAL_LOOP_THE_FIRST_X_8 \ + ++y; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_FIRST_X_8 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_FIRST_X_32 \ + EXTERNAL_LOOP_THE_FIRST_X_16 \ + ++y; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_FIRST_X_16 + +/* + assert ((0 <= xI) && (xI <= up->getLx() - 1) && \ + (0 <= yI) && (yI <= up->getLy() - 1) ); \ +*/ + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND \ + xL += deltaXL; \ + xI = xL >> PADN; \ + upPix00 = upBasePix + (yI * upWrap + xI); \ + upPix10 = upPix00 + 1; \ + upPix01 = upPix00 + upWrap; \ + upPix11 = upPix00 + upWrap + 1; \ + xWeight1 = (xL & MASKN); \ + xWeight0 = (1 << PADN) - xWeight1; \ + c1 = MULT_2_X_16BIT(upPix00->r, upPix00->g, xWeight0); \ + c3 = MULT_2_X_16BIT(upPix00->b, upPix01->r, xWeight0); \ + c5 = MULT_2_X_16BIT(upPix01->g, upPix01->b, xWeight0); \ + c2 = MULT_2_X_16BIT(upPix10->r, upPix10->g, xWeight1); \ + c4 = MULT_2_X_16BIT(upPix10->b, upPix11->r, xWeight1); \ + c6 = MULT_2_X_16BIT(upPix11->g, upPix11->b, xWeight1); \ + s_gb = (c5 + c6) >> 16; \ + gColUpTmp = s_gb >> 32; \ + bColUpTmp = s_gb & 0x1FF; \ + s_br = (c3 + c4) >> 16; \ + bColDownTmp = s_br >> 32; \ + rColUpTmp = s_br & 0x1FF; \ + s_rg = (c1 + c2) >> 16; \ + rColDownTmp = s_rg >> 32; \ + gColDownTmp = s_rg & 0x1FF; \ + rCol = \ + (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); \ + gCol = \ + (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); \ + bCol = \ + (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); \ + *dnPix = TPixel32(rCol, gCol, bCol, upPix00->m); + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND_X_2 \ + INTERNAL_LOOP_THE_SECOND \ + ++dnPix; \ + INTERNAL_LOOP_THE_SECOND + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND_X_4 \ + INTERNAL_LOOP_THE_SECOND_X_2 \ + ++dnPix; \ + INTERNAL_LOOP_THE_SECOND_X_2 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND_X_8 \ + INTERNAL_LOOP_THE_SECOND_X_4 \ + ++dnPix; \ + INTERNAL_LOOP_THE_SECOND_X_4 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND_X_16 \ + INTERNAL_LOOP_THE_SECOND_X_8 \ + ++dnPix; \ + INTERNAL_LOOP_THE_SECOND_X_8 + +// ------------------------------------------------------------------------------------------------- + +#define INTERNAL_LOOP_THE_SECOND_X_32 \ + INTERNAL_LOOP_THE_SECOND_X_16 \ + ++dnPix; \ + INTERNAL_LOOP_THE_SECOND_X_16 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND \ + xL = xL0 + (kMinX - 1) * deltaXL; \ + yL += deltaYL; \ + yI = yL >> PADN; \ + yWeight1 = (yL & MASKN); \ + yWeight0 = (1 << PADN) - yWeight1; \ + dnPix = dnRow + xMin + kMinX; \ + dnEndPix = dnRow + xMin + kMaxX + 1; \ + for (; dnPix < dnEndPix - 32; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND_X_32; \ + } \ + for (; dnPix < dnEndPix - 16; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND_X_16; \ + } \ + for (; dnPix < dnEndPix - 8; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND_X_8; \ + } \ + for (; dnPix < dnEndPix - 4; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND_X_4; \ + } \ + for (; dnPix < dnEndPix - 2; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND_X_2; \ + } \ + for (; dnPix < dnEndPix; ++dnPix) { \ + INTERNAL_LOOP_THE_SECOND \ + } + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND_X_2 \ + EXTERNAL_LOOP_THE_SECOND \ + ++kY; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_SECOND + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND_X_4 \ + EXTERNAL_LOOP_THE_SECOND_X_2 \ + ++kY; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_SECOND_X_2 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND_X_8 \ + EXTERNAL_LOOP_THE_SECOND_X_4 \ + ++kY; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_SECOND_X_4 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND_X_16 \ + EXTERNAL_LOOP_THE_SECOND_X_8 \ + ++kY; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_SECOND_X_8 + +// ------------------------------------------------------------------------------------------------- + +#define EXTERNAL_LOOP_THE_SECOND_X_32 \ + EXTERNAL_LOOP_THE_SECOND_X_16 \ + ++kY; \ + dnRow += dnWrap; \ + EXTERNAL_LOOP_THE_SECOND_X_16 + +#endif diff --git a/toonz/sources/common/trop/optimize_for_lp64.h b/toonz/sources/common/trop/optimize_for_lp64.h new file mode 100644 index 0000000..0737b73 --- /dev/null +++ b/toonz/sources/common/trop/optimize_for_lp64.h @@ -0,0 +1,185 @@ + + +#ifndef OPTIMIZE_FOR_LP64_INCLUDED +#define OPTIMIZE_FOR_LP64_INCLUDED + +/* ========================================================================= */ + +/* + + ***************************************************************************** + * OSSERVAZIONI * + ***************************************************************************** + + ____________OSS 1:___________________________________________________________ + + + se devo fare DUE MOLTIPLICAZIONI 13 bit * 8 bit posso farle in un + colpo solo, ad esempio: + + siano X = xxxxxxxxxxxxx + S = ssssssss + Y = yyyyyyyyyyyyy + T = tttttttt + + e devo calcolare + U = X * S + V = Y * T + posso farlo in un colpo solo impacchettando i bit cosi': + + A = X 0 00000000 Y = xxxxxxxxxxxxx 0 00000000 yyyyyyyyyyyyy + B = 00000 S 0 00000000 00000 T = 00000ssssssss 0 00000000 00000tttttttt + + ora se faccio C = A * B si ha + + C = U ?????????????????????? V = + = uuuuuuuuuuuuuuuuuuuuu ?????????????????????? vvvvvvvvvvvvvvvvvvvvv + + dove C e' di 64 bit; cioe' i primi 21 bit sono X * S = U + e gli ultimi 21 sono Y * T = V + + ____________OSS 2:___________________________________________________________ + + + se devo fare DUE MOLTIPLICAZIONI 16 bit * 16 bit del tipo + X * S = U + Y * S = V + + con + + #X = 16, + #Y = 16, + #S = 16 + + (dove l'operatore '#' da' come risultato il numero di bit di cui e' composto + un numero intero) + + posso farle tutte e due in un solo colpo impacchettando i bit cosi': + + O = 0000000000000000, #O = 16 + A = X O Y , #A = 48 + B = S , #B = 16 + C = A * B , #C = 64 + + dove i primi 32 bit sono X * S e i secondi 32 bit sono Y * S + + ____________OSS 3:___________________________________________________________ + + + se devo fare QUATTRO MOLTIPLICAZIONI 8 bit * 8 bit del tipo + X * S = I #X = 8, #S = 8, #I = 16 + Y * S = J #Y = 8, #S = 8, #J = 16 + Z * S = K #Z = 8, #S = 8, #K = 16 + W * S = L #W = 8, #S = 8, #L = 16 + + + posso farle tutte e due in un solo colpo impacchettando i bit cosi': + + O = 00000000 #O = 8 + C = XOYOZOW * OOOOOOS #C = 64 + + dove + I sono i primi 16 bit, + J sono i secondi 16 bit, + K sono i terzi 16 bit, + L i quarti 16 bit + _____________________________________________________________________________ + + ***************************************************************************** + */ + +/* ========================================================================= */ + +#define OPTIMIZE_FOR_LP64 + +/* ========================================================================= */ + +#define MASK_FIRST_OF_3_X_16BIT 0x7FFFC00000000 +#define MASK_SECOND_OF_3_X_16BIT 0x3FFFE0000 +#define MASK_THIRD_OF_3_X_16BIT 0x1FFFF + +#define FIRST_OF_3_X_16BIT(x) (x) >> 34 +#define SECOND_OF_3_X_16BIT(x) ((x)&MASK_SECOND_OF_3_X_16BIT) >> 17; +#define THIRD_OF_3_X_16BIT(x) (x) & MASK_THIRD_OF_3_X_16BIT; + +/* ========================================================================= */ + +#define MASK_FIRST_OF_2_X_24BIT 0x3FFFFFE000000 +#define MASK_SECOND_OF_2_X_24BIT 0x1FFFFFF + +#define FIRST_OF_2_X_24BIT(x) (x) >> 25 +#define SECOND_OF_2_X_24BIT(x) (x) & MASK_SECOND_OF_2_X_24BIT + +/* ========================================================================= */ + +#define MASK_FIRST_OF_2_X_32BIT 0xFFFFFFFF00000000 +#define MASK_SECOND_OF_2_X_32BIT 0xFFFFFFFF + +#define FIRST_OF_2_X_32BIT(x) (x) >> 32 +#define SECOND_OF_2_X_32BIT(x) (x) & MASK_SECOND_OF_2_X_32BIT + +/* ========================================================================= */ + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT24; +typedef unsigned int UINT32; +typedef unsigned long UINT50; +typedef unsigned long UINT51; +typedef unsigned long UINT64; + +/* ========================================================================= */ + +#if 0 + +/* esegue a1+b1, a2+c2, a3+c3 in un'unica operazione */ +UINT64 add_3_x_16bit ( UINT16 a1, UINT16 a2, UINT16 a3, + UINT16 b1, UINT16 b2, UINT16 b3 ); + +/* esegue a1+b1, a2+b2 in un'unica operazione */ +UINT50 add_2_x_24bit ( UINT24 a1, UINT24 a2, + UINT24 b1, UINT24 b2 ); + +/* esegue a1*b, a2*b in un'unica operazione */ +UINT64 mult_2_x_16bit ( UINT16 a1, UINT16 a2, + UINT16 b ); +#endif + +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ + +#define ADD_3_X_16BIT(a1, a2, a3, b1, b2, b3) \ + (0L | (UINT64)(a1) << 34 | (UINT64)(a2) << 17 | (a3)) + \ + (0L | (UINT64)(b1) << 34 | (UINT64)(b2) << 17 | (b3)) + +inline UINT64 add_3_x_16bit(UINT16 a1, UINT16 a2, UINT16 a3, + UINT16 b1, UINT16 b2, UINT16 b3) +{ + return (0L | (UINT64)a1 << 34 | (UINT64)a2 << 17 | a3) + + (0L | (UINT64)b1 << 34 | (UINT64)b2 << 17 | b3); +} + +/* ------------------------------------------------------------------------- */ + +#define ADD_2_X_24BIT(a1, a2, b1, b2) \ + (0L | (UINT64)(a1) << 25 | (a2)) + (0L | (UINT64)(b1) << 25 | (b2)) + +inline UINT50 add_2_x_24bit(UINT24 a1, UINT24 a2, + UINT24 b1, UINT24 b2) +{ + return (0L | (UINT64)a1 << 25 | a2) + + (0L | (UINT64)b1 << 25 | b2); +} + +/* ------------------------------------------------------------------------- */ + +#define MULT_2_X_16BIT(a1, a2, b) ((UINT64)b) * (((UINT64)(a1) << 32) | (UINT64)a2) + +inline UINT64 mult_2_x_16bit(UINT16 a1, UINT16 a2, + UINT16 b) +{ + return (0L | (UINT64)a1 << 32 | a2) * b; +} + +#endif diff --git a/toonz/sources/common/trop/pixelselectors.h b/toonz/sources/common/trop/pixelselectors.h new file mode 100644 index 0000000..be41e61 --- /dev/null +++ b/toonz/sources/common/trop/pixelselectors.h @@ -0,0 +1,130 @@ + + +#ifndef PIXEL_SELECTORS_H +#define PIXEL_SELECTORS_H + +#include "tpixel.h" +#include "tpixelgr.h" +#include "tpixelcm.h" + +namespace TRop +{ +namespace borders +{ + +//**************************************************************** +// Standard Pixel Selectors +//**************************************************************** + +template +class PixelSelector +{ + bool m_skip; + +public: + typedef Pix pixel_type; + typedef Pix value_type; + +public: + PixelSelector(bool onlyCorners = true) + : m_skip(onlyCorners) {} + + value_type transparent() const { return pixel_type::Transparent; } + bool transparent(const pixel_type &pix) const { return (pix.m == 0); } + + value_type value(const pixel_type &pix) const { return pix; } + bool equal(const pixel_type &a, const pixel_type &b) const { return a == b; } + + void setSkip(bool skip) { m_skip = skip; } + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return m_skip; } +}; + +//-------------------------------------------------------------------------------- + +template <> +class PixelSelector +{ + bool m_skip; + TPixelGR8 m_transpColor; + +public: + typedef TPixelGR8 pixel_type; + typedef TPixelGR8 value_type; + +public: + PixelSelector(bool onlyCorners = true, + pixel_type transparentColor = pixel_type::White) + : m_skip(onlyCorners), m_transpColor(transparentColor) {} + + value_type transparent() const { return m_transpColor; } + bool transparent(const pixel_type &pix) const { return (pix == m_transpColor); } + + value_type value(const pixel_type &pix) const { return pix; } + bool equal(const pixel_type &a, const pixel_type &b) const { return a == b; } + + void setSkip(bool skip) { m_skip = skip; } + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return m_skip; } +}; + +//-------------------------------------------------------------------------------- + +template <> +class PixelSelector +{ + bool m_skip; + TPixelGR16 m_transpColor; + +public: + typedef TPixelGR16 pixel_type; + typedef TPixelGR16 value_type; + +public: + PixelSelector(bool onlyCorners = true, + pixel_type transparentColor = pixel_type::White) + : m_skip(onlyCorners), m_transpColor(transparentColor) {} + + value_type transparent() const { return m_transpColor; } + bool transparent(const pixel_type &pix) const { return (pix == m_transpColor); } + + value_type value(const pixel_type &pix) const { return pix; } + bool equal(const pixel_type &a, const pixel_type &b) const { return a == b; } + + void setSkip(bool skip) { m_skip = skip; } + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return m_skip; } +}; + +//-------------------------------------------------------------------------------- + +template <> +class PixelSelector +{ + int m_tone; + bool m_skip; + +public: + typedef TPixelCM32 pixel_type; + typedef TUINT32 value_type; + +public: + PixelSelector(bool onlyCorners = true, int tone = 128) + : m_tone(tone), m_skip(onlyCorners) {} + + value_type transparent() const { return 0; } + bool transparent(const pixel_type &pix) const { return value(pix) == 0; } + + value_type value(const pixel_type &pix) const + { + return (pix.getTone() < m_tone) ? pix.getInk() : pix.getPaint(); + } + bool equal(const pixel_type &a, const pixel_type &b) const + { + return value(a) == value(b); + } + + void setSkip(bool skip) { m_skip = skip; } + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return m_skip; } +}; +} +} //namespace TRop::borders + +#endif //PIXEL_SELECTORS_H diff --git a/toonz/sources/common/trop/quickput.cpp b/toonz/sources/common/trop/quickput.cpp new file mode 100644 index 0000000..041e6d2 --- /dev/null +++ b/toonz/sources/common/trop/quickput.cpp @@ -0,0 +1,4656 @@ + + +#include "trop.h" +#include "loop_macros.h" +#include "tpixelutils.h" + +#ifndef TNZCORE_LIGHT +#include "tpalette.h" +#include "tcolorstyles.h" +#endif + +/* +#ifndef __sgi +#include +#endif +*/ + +//The following must be old IRIX code. Should be re-tested. +//It seems that gcc compiles it, but requiring a LOT of +//resources... very suspect... + +/*#ifdef __LP64__ +#include "optimize_for_lp64.h" +#endif*/ + +//============================================================================= +//============================================================================= +//============================================================================= + +#ifdef OPTIMIZE_FOR_LP64 +void quickResample_optimized( + const TRasterP &dn, + const TRasterP &up, + const TAffine &aff, + TRop::ResampleFilterType filterType); +#endif + +namespace +{ + +inline TPixel32 applyColorScale(const TPixel32 &color, const TPixel32 &colorScale, bool toBePremultiplied = false) +{ + /*-- 半透明のラスタをViewer上で半透明にquickputするとき、色が暗くなってしまうのを防ぐ --*/ + if (colorScale.r == 0 && colorScale.g == 0 && colorScale.b == 0) { + /*-- toBePremultipliedがONのときは、後でPremultiplyをするので、ここでは行わない --*/ + if (toBePremultiplied) + return TPixel32(color.r, color.g, color.b, color.m * colorScale.m / 255); + else + return TPixel32(color.r * colorScale.m / 255, color.g * colorScale.m / 255, color.b * colorScale.m / 255, color.m * colorScale.m / 255); + } + int r = color.r + colorScale.r; + int g = color.g + colorScale.g; + int b = color.b + colorScale.b; + + return premultiply(TPixel32(r > 255 ? 255 : r, g > 255 ? 255 : g, b > 255 ? 255 : b, color.m * colorScale.m / 255)); +} + +//------------------------------------------------------------------------------ + +inline TPixel32 applyColorScaleCMapped(const TPixel32 &color, const TPixel32 &colorScale) +{ + int r = color.r + colorScale.r; + int g = color.g + colorScale.g; + int b = color.b + colorScale.b; + + return premultiply(TPixel32(r > 255 ? 255 : r, g > 255 ? 255 : g, b > 255 ? 255 : b, color.m * colorScale.m / 255)); +} + +//------------------------------------------------------------------------------ + +void doQuickPutFilter( + const TRaster32P &dn, + const TRaster32P &up, + const TAffine &aff) +{ + // se aff e' degenere la controimmagine di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel successivo + // comporta l'incremento (deltaXD, deltaYD) delle coordinate del pixel + // corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // naturale predecessore di up->getLx() - 1 + int lxPred = (up->getLx() - 2) * (1 << PADN); + + // naturale predecessore di up->getLy() - 1 + int lyPred = (up->getLy() - 2) * (1 << PADN); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + TPixel32 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima + // scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante + // invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax + // con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente intersecando la (2) + // con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff + // della porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + // + // + // 0 <= yL0 + k*deltaYL <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up contratto + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + if (lxPred < xL0) // [a, b] esterno ad up+(bordo destro) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + if (xL0 < 0) // [a, b] esterno ad up contratto + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up contratto + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + if (lyPred < yL0) // [a, b] esterno ad up contratto + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + if (yL0 < 0) // [a, b] esterno ad up contratto + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clipping su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" + // del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // troncato + int yI = yL >> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixel32 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixel32 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixel32 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixel32 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo dei pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int rColDownTmp = (xWeight0 * (upPix00->r) + + xWeight1 * ((upPix10)->r)) >> + PADN; + + int gColDownTmp = (xWeight0 * (upPix00->g) + + xWeight1 * ((upPix10)->g)) >> + PADN; + + int bColDownTmp = (xWeight0 * (upPix00->b) + + xWeight1 * ((upPix10)->b)) >> + PADN; + + int rColUpTmp = (xWeight0 * ((upPix01)->r) + + xWeight1 * ((upPix11)->r)) >> + PADN; + + int gColUpTmp = (xWeight0 * ((upPix01)->g) + + xWeight1 * ((upPix11)->g)) >> + PADN; + + int bColUpTmp = (xWeight0 * ((upPix01)->b) + + xWeight1 * ((upPix11)->b)) >> + PADN; + + unsigned char rCol = + (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); + + unsigned char gCol = + (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); + + unsigned char bCol = + (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); + + TPixel32 upPix = TPixel32(rCol, gCol, bCol, upPix00->m); + + if (upPix.m == 0) + continue; + else if (upPix.m == 255) + *dnPix = upPix; + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickPutNoFilter( + const TRaster32P &dn, + const TRaster32P &up, + const TAffine &aff, + const TPixel32 &colorScale, + bool doPremultiply, + bool whiteTransp, + bool firstColumn, + bool doRasterDarkenBlendedView) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixel32 upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) + upPix.m = 255; + if (upPix.m == 0 || (whiteTransp && upPix == TPixel::White)) + continue; + + if (colorScale != TPixel32::Black) + upPix = applyColorScale(upPix, colorScale, doPremultiply); + + if (doRasterDarkenBlendedView) + *dnPix = quickOverPixDarkenBlended(*dnPix, upPix); + else { + if (upPix.m == 255) + *dnPix = upPix; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix); + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickPutNoFilter( + const TRaster32P &dn, + const TRaster64P &up, + const TAffine &aff, + bool doPremultiply, + bool firstColumn) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel64 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixel64 *upPix = upBasePix + (yI * upWrap + xI); + if (firstColumn) + upPix->m = 65535; + if (upPix->m == 0) + continue; + else if (upPix->m == 65535) + *dnPix = PixelConverter::from(*upPix); + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, PixelConverter::from(*upPix)); + else + *dnPix = quickOverPix(*dnPix, PixelConverter::from(*upPix)); + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickPutNoFilter( + const TRaster32P &dn, + const TRasterGR8P &up, + const TAffine &aff, + const TPixel32 &colorScale) +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + const int PADN = 16; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); + + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + int deltaXL = tround(deltaXD * (1 << PADN)); + + int deltaYL = tround(deltaYD * (1 << PADN)); + + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = up->getLx() * (1 << PADN) - 1; + + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixelGR8 *upBasePix = up->pixels(); + + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + + TPointD a = invAff * TPointD(xMin, y); + + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = xMax - xMin; // clipping su dn + + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixelGR8 *upPix = upBasePix + (yI * upWrap + xI); + if (colorScale == TPixel32::Black) { + + if (upPix->value == 0) + dnPix->r = dnPix->g = dnPix->b = 0; + else if (upPix->value == 255) + dnPix->r = dnPix->g = dnPix->b = upPix->value; + else + *dnPix = quickOverPix(*dnPix, *upPix); + dnPix->m = 255; + } else { + TPixel32 upPix32(upPix->value, upPix->value, upPix->value, 255); + upPix32 = applyColorScale(upPix32, colorScale); + + if (upPix32.m == 255) + *dnPix = upPix32; + else + *dnPix = quickOverPix(*dnPix, upPix32); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= + +void doQuickPutFilter( + const TRaster32P &dn, + const TRaster32P &up, + double sx, double sy, + double tx, double ty) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine + // mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up (con gli estremi eventualmente invertiti) e' + // la controimmagine + // mediante aff della porzione di scanline [ (xMin, yMin), (xMax, yMin) ] + // di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) in + // versione "TLonghizzata" + // + // 0 <= xL0 + kX*deltaXL + // <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 1) + int lxPred = (up->getLx() - 2) * (1 << PADN); + + // TINT32 predecessore di (up->getLy() - 1) + int lyPred = (up->getLy() - 2) * (1 << PADN); + + // 0 <= xL0 + k*deltaXL + // <= (up->getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + // + // 0 <= yL0 + k*deltaYL + // <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con + // i lati (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up contratto + assert(yL0 <= lyPred); + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con + // i lati (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up contratto + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + + // inizializza yL + int yL = yL0 + (kMinY - 1) * deltaYL; + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + // inizializza xL + int xL = xL0 + (kMinX - 1) * deltaXL; + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + // filtro bilineare 4 pixels: calcolo degli y-pesi + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixel32 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixel32 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixel32 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixel32 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo degli x-pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int rColDownTmp = + (xWeight0 * (upPix00->r) + xWeight1 * ((upPix10)->r)) >> PADN; + + int gColDownTmp = + (xWeight0 * (upPix00->g) + xWeight1 * ((upPix10)->g)) >> PADN; + + int bColDownTmp = + (xWeight0 * (upPix00->b) + xWeight1 * ((upPix10)->b)) >> PADN; + + int rColUpTmp = + (xWeight0 * ((upPix01)->r) + xWeight1 * ((upPix11)->r)) >> PADN; + + int gColUpTmp = + (xWeight0 * ((upPix01)->g) + xWeight1 * ((upPix11)->g)) >> PADN; + + int bColUpTmp = + (xWeight0 * ((upPix01)->b) + xWeight1 * ((upPix11)->b)) >> PADN; + + unsigned char rCol = + (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); + + unsigned char gCol = + (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); + + unsigned char bCol = + (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); + + TPixel32 upPix = TPixel32(rCol, gCol, bCol, upPix00->m); + + if (upPix.m == 0) + continue; + else if (upPix.m == 255) + *dnPix = upPix; + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= +void doQuickPutNoFilter( + const TRaster32P &dn, + const TRaster32P &up, + double sx, double sy, + double tx, double ty, + const TPixel32 &colorScale, + bool doPremultiply, bool whiteTransp, bool firstColumn, + bool doRasterDarkenBlendedView) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine + // mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + // 0 <= xL0 + k*deltaXL < up->getLx()*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + + // inizializza yL + int yL = yL0 + (kMinY - 1) * deltaYL; + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + // inizializza xL + int xL = xL0 + (kMinX - 1) * deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixel32 upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) + upPix.m = 65535; + + if (upPix.m == 0 || (whiteTransp && upPix == TPixel::White)) + continue; + + if (colorScale != TPixel32::Black) + upPix = applyColorScale(upPix, colorScale, doPremultiply); + + if (doRasterDarkenBlendedView) + *dnPix = quickOverPixDarkenBlended(*dnPix, upPix); + else { + if (upPix.m == 255) + *dnPix = upPix; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix); + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +void doQuickPutNoFilter( + const TRaster32P &dn, + const TRasterGR8P &up, + double sx, double sy, + double tx, double ty, + const TPixel32 &colorScale) +{ + if ((sx == 0) || (sy == 0)) + return; + + const int PADN = 16; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + int deltaXL = tround(deltaXD * (1 << PADN)); + int deltaYL = tround(deltaYD * (1 << PADN)); + if ((deltaXL == 0) || (deltaYL == 0)) + return; + TPointD a = invAff * TPointD(xMin, yMin); + + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = yMax - yMin; // clipping su dn + int lxPred = up->getLx() * (1 << PADN) - 1; + int lyPred = up->getLy() * (1 << PADN) - 1; + + if (deltaYL > 0) // (deltaYL != 0) + { + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + if (deltaXL > 0) // (deltaXL != 0) + { + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixelGR8 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + int yL = yL0 + (kMinY - 1) * deltaYL; + + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + // inizializza xL + int xL = xL0 + (kMinX - 1) * deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + int xI = xL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixelGR8 *upPix = upBasePix + (yI * upWrap + xI); + if (colorScale == TPixel32::Black) { + dnPix->r = dnPix->g = dnPix->b = upPix->value; + dnPix->m = 255; + } else { + TPixel32 upPix32(upPix->value, upPix->value, upPix->value, 255); + upPix32 = applyColorScale(upPix32, colorScale); + + if (upPix32.m == 255) + *dnPix = upPix32; + else + *dnPix = quickOverPix(*dnPix, upPix32); + } + + /* + if (upPix->value == 0) + dnPix->r = dnPix->g = dnPix->b = dnPix->m = upPix->value; + else if (upPix->value == 255) + dnPix->r = dnPix->g = dnPix->b = dnPix->m = upPix->value; + else + *dnPix = quickOverPix(*dnPix, *upPix); + */ + } + } + dn->unlock(); + up->unlock(); +} + +void doQuickResampleFilter( + const TRaster32P &dn, + const TRaster32P &up, + const TAffine &aff) +{ + // se aff e' degenere la controimmagine di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate + // del pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // naturale predecessore di up->getLx() - 1 + int lxPred = (up->getLx() - 2) * (1 << PADN); + + // naturale predecessore di up->getLy() - 1 + int lyPred = (up->getLy() - 2) * (1 << PADN); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + // + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax + // con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente intersecando + // la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up contratto + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro) + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up contratto + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up contratto + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up contratto + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up contratto + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "longhizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + int yI = yL >> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixel32 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixel32 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixel32 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixel32 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo dei pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int rColDownTmp = + (xWeight0 * (upPix00->r) + xWeight1 * ((upPix10)->r)) >> PADN; + + int gColDownTmp = + (xWeight0 * (upPix00->g) + xWeight1 * ((upPix10)->g)) >> PADN; + + int bColDownTmp = + (xWeight0 * (upPix00->b) + xWeight1 * ((upPix10)->b)) >> PADN; + + int mColDownTmp = + (xWeight0 * (upPix00->m) + xWeight1 * ((upPix10)->m)) >> PADN; + + int rColUpTmp = + (xWeight0 * ((upPix01)->r) + xWeight1 * ((upPix11)->r)) >> PADN; + + int gColUpTmp = + (xWeight0 * ((upPix01)->g) + xWeight1 * ((upPix11)->g)) >> PADN; + + int bColUpTmp = + (xWeight0 * ((upPix01)->b) + xWeight1 * ((upPix11)->b)) >> PADN; + + int mColUpTmp = + (xWeight0 * ((upPix01)->m) + xWeight1 * ((upPix11)->m)) >> PADN; + + dnPix->r = (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); + dnPix->g = (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); + dnPix->b = (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); + dnPix->m = (unsigned char)((yWeight0 * mColDownTmp + yWeight1 * mColUpTmp) >> PADN); + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickResampleFilter( + const TRaster32P &dn, + const TRasterGR8P &up, + const TAffine &aff) +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + const int PADN = 16; + + const int MASKN = (1 << PADN) - 1; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); + int deltaYL = tround(deltaYD * (1 << PADN)); + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = (up->getLx() - 2) * (1 << PADN); + int lyPred = (up->getLy() - 2) * (1 << PADN); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixelGR8 *upBasePix = up->pixels(); + + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + TPointD a = invAff * TPointD(xMin, y); + int xL0 = tround(a.x * (1 << PADN)); + int yL0 = tround(a.y * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = xMax - xMin; // clipping su dn + + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else { + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + if (deltaYL == 0) { + if ((yL0 < 0) || (lyPred < yL0)) + continue; + } else if (deltaYL > 0) { + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + int xI = xL >> PADN; // troncato + int yI = yL >> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixelGR8 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixelGR8 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixelGR8 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixelGR8 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo dei pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int colDownTmp = + (xWeight0 * (upPix00->value) + xWeight1 * ((upPix10)->value)) >> PADN; + + int colUpTmp = + (xWeight0 * ((upPix01)->value) + xWeight1 * ((upPix11)->value)) >> PADN; + + dnPix->r = dnPix->g = dnPix->b = (unsigned char)((yWeight0 * colDownTmp + yWeight1 * colUpTmp) >> PADN); + + dnPix->m = 255; + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickResampleColorFilter( + const TRaster32P &dn, + const TRaster32P &up, + const TAffine &aff, + UCHAR colorMask) +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + const int PADN = 16; + + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); // clipping y su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); // clipping x su dn + + TAffine invAff = inv(aff); // inversa di aff + + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = up->getLx() * (1 << PADN) - 1; // TINT32 predecessore di up->getLx() + int lyPred = up->getLy() * (1 << PADN) - 1; // TINT32 predecessore di up->getLy() + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + TPointD a = invAff * TPointD(xMin, y); + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = xMax - xMin; // clipping su dn + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } else // (deltaXL < 0) + { + if (xL0 < 0) + continue; + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + if (deltaYL == 0) { + if ((yL0 < 0) || (lyPred < yL0)) + continue; + } else if (deltaYL > 0) { + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } else // (deltaYL < 0) + { + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + int xI = xL >> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + if (colorMask == TRop::MChan) + dnPix->r = dnPix->g = dnPix->b = (upBasePix + (yI * upWrap + xI))->m; + else { + TPixel32 *pix = upBasePix + (yI * upWrap + xI); + dnPix->r = ((colorMask & TRop::RChan) ? pix->r : 0); + dnPix->g = ((colorMask & TRop::GChan) ? pix->g : 0); + dnPix->b = ((colorMask & TRop::BChan) ? pix->b : 0); + } + dnPix->m = 255; + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= + +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickResampleColorFilter( + const TRaster32P &dn, + const TRaster64P &up, + const TAffine &aff, + UCHAR colorMask) +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + const int PADN = 16; + + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); // clipping y su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); // clipping x su dn + + TAffine invAff = inv(aff); // inversa di aff + + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = up->getLx() * (1 << PADN) - 1; // TINT32 predecessore di up->getLx() + int lyPred = up->getLy() * (1 << PADN) - 1; // TINT32 predecessore di up->getLy() + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel64 *upBasePix = up->pixels(); + + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + TPointD a = invAff * TPointD(xMin, y); + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = xMax - xMin; // clipping su dn + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } else // (deltaXL < 0) + { + if (xL0 < 0) + continue; + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + if (deltaYL == 0) { + if ((yL0 < 0) || (lyPred < yL0)) + continue; + } else if (deltaYL > 0) { + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } else // (deltaYL < 0) + { + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + int xI = xL >> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + if (colorMask == TRop::MChan) + dnPix->r = dnPix->g = dnPix->b = byteFromUshort((upBasePix + (yI * upWrap + xI))->m); + else { + TPixel64 *pix = upBasePix + (yI * upWrap + xI); + dnPix->r = byteFromUshort(((colorMask & TRop::RChan) ? pix->r : 0)); + dnPix->g = byteFromUshort(((colorMask & TRop::GChan) ? pix->g : 0)); + dnPix->b = byteFromUshort(((colorMask & TRop::BChan) ? pix->b : 0)); + } + dnPix->m = 255; + } + } + dn->unlock(); + up->unlock(); +} +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickResampleFilter( + const TRaster32P &dn, + const TRaster32P &up, + double sx, double sy, + double tx, double ty) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - /*1*/ 2, up->getLy() - /*1*/ 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' + // un segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up (con gli estremi eventualmente invertiti) e' + // la controimmagine mediante aff della porzione di scanline + // [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 1) + int lxPred = (up->getLx() - /*1*/ 2) * (1 << PADN); + + // TINT32 predecessore di (up->getLy() - 1) + int lyPred = (up->getLy() - /*1*/ 2) * (1 << PADN); + + // 0 <= xL0 + k*deltaXL <= (up->getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up contratto + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up contratto + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + + dn->lock(); + up->lock(); + TPixel32 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" + // del pixel corrente di up + int yL = yL0 + (kMinY - 1) * deltaYL; // inizializza yL + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + int xL = xL0 + (kMinX - 1) * deltaXL; // inizializza xL + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + // filtro bilineare 4 pixels: calcolo degli y-pesi + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixel32 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixel32 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixel32 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixel32 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo degli x-pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int rColDownTmp = + (xWeight0 * (upPix00->r) + xWeight1 * ((upPix10)->r)) >> PADN; + + int gColDownTmp = + (xWeight0 * (upPix00->g) + xWeight1 * ((upPix10)->g)) >> PADN; + + int bColDownTmp = + (xWeight0 * (upPix00->b) + xWeight1 * ((upPix10)->b)) >> PADN; + + int mColDownTmp = + (xWeight0 * (upPix00->m) + xWeight1 * ((upPix10)->m)) >> PADN; + + int rColUpTmp = + (xWeight0 * ((upPix01)->r) + xWeight1 * ((upPix11)->r)) >> PADN; + + int gColUpTmp = + (xWeight0 * ((upPix01)->g) + xWeight1 * ((upPix11)->g)) >> PADN; + + int bColUpTmp = + (xWeight0 * ((upPix01)->b) + xWeight1 * ((upPix11)->b)) >> PADN; + + int mColUpTmp = + (xWeight0 * ((upPix01)->m) + xWeight1 * ((upPix11)->m)) >> PADN; + + dnPix->r = (unsigned char)((yWeight0 * rColDownTmp + yWeight1 * rColUpTmp) >> PADN); + dnPix->g = (unsigned char)((yWeight0 * gColDownTmp + yWeight1 * gColUpTmp) >> PADN); + dnPix->b = (unsigned char)((yWeight0 * bColDownTmp + yWeight1 * bColUpTmp) >> PADN); + dnPix->m = (unsigned char)((yWeight0 * mColDownTmp + yWeight1 * mColUpTmp) >> PADN); + } + } + dn->unlock(); + up->unlock(); +} + +//------------------------------------------------------------------------------------------ + +void doQuickResampleFilter( + const TRaster32P &dn, + const TRasterGR8P &up, + double sx, double sy, + double tx, double ty) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - /*1*/ 2, up->getLy() - /*1*/ 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' + // un segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up (con gli estremi eventualmente invertiti) e' + // la controimmagine mediante aff della porzione di scanline + // [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 1) + int lxPred = (up->getLx() - /*1*/ 2) * (1 << PADN); + + // TINT32 predecessore di (up->getLy() - 1) + int lyPred = (up->getLy() - /*1*/ 2) * (1 << PADN); + + // 0 <= xL0 + k*deltaXL <= (up->getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up contratto + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up contratto + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + + dn->lock(); + up->lock(); + TPixelGR8 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" + // del pixel corrente di up + int yL = yL0 + (kMinY - 1) * deltaYL; // inizializza yL + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + int xL = xL0 + (kMinX - 1) * deltaXL; // inizializza xL + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + // filtro bilineare 4 pixels: calcolo degli y-pesi + int yWeight1 = (yL & MASKN); + int yWeight0 = (1 << PADN) - yWeight1; + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // troncato + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + // (xI, yI) + TPixelGR8 *upPix00 = upBasePix + (yI * upWrap + xI); + + // (xI + 1, yI) + TPixelGR8 *upPix10 = upPix00 + 1; + + // (xI, yI + 1) + TPixelGR8 *upPix01 = upPix00 + upWrap; + + // (xI + 1, yI + 1) + TPixelGR8 *upPix11 = upPix00 + upWrap + 1; + + // filtro bilineare 4 pixels: calcolo degli x-pesi + int xWeight1 = (xL & MASKN); + int xWeight0 = (1 << PADN) - xWeight1; + + // filtro bilineare 4 pixels: media pesata sui singoli canali + int colDownTmp = + (xWeight0 * (upPix00->value) + xWeight1 * (upPix10->value)) >> PADN; + + int colUpTmp = + (xWeight0 * ((upPix01)->value) + xWeight1 * (upPix11->value)) >> PADN; + + dnPix->m = 255; + dnPix->r = dnPix->g = dnPix->b = (unsigned char)((yWeight0 * colDownTmp + yWeight1 * colUpTmp) >> PADN); + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +//============================================================================= +template +void doQuickResampleNoFilter( + const TRasterPT &dn, + const TRasterPT &up, + double sx, double sy, + double tx, double ty) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL < up->getLx()*(1<getLy()*(1<getLx() * (1 << PADN) - 1; // TINT32 predecessore di up->getLx() + int lyPred = up->getLy() * (1 << PADN) - 1; // TINT32 predecessore di up->getLy() + + // 0 <= xL0 + k*deltaXL < up->getLx()*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + assert(yL0 <= lyPred); // [a, b] interno ad up+(bordo destro/basso) + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + PIX *upBasePix = up->pixels(); + PIX *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int yL = yL0 + (kMinY - 1) * deltaYL; // inizializza yL + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + int xL = xL0 + (kMinX - 1) * deltaXL; // inizializza xL + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // round + + PIX *dnPix = dnRow + xMin + kMinX; + PIX *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + *dnPix = *(upBasePix + (yI * upWrap + xI)); + } + } + + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +//============================================================================= + +#ifndef TNZCORE_LIGHT + +//============================================================================= +//============================================================================= +//============================================================================= +//============================================================================= +// +// doQuickPutCmapped +// +//============================================================================= + +void doQuickPutCmapped( + const TRaster32P &dn, + const TRasterCM32P &up, + const TPaletteP &palette, + const TAffine &aff, + const TPixel32 &globalColorScale, + bool inksOnly) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + + vector colors(palette->getStyleCount()); + //vector inks(palette->getStyleCount()); + + if (globalColorScale != TPixel::Black) + for (int i = 0; i < palette->getStyleCount(); i++) + colors[i] = applyColorScaleCMapped(palette->getStyle(i)->getAverageColor(), globalColorScale); + else + for (int i = 0; i < palette->getStyleCount(); i++) + colors[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixelCM32 *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixelCM32 *upPix = upBasePix + (yI * upWrap + xI); + int t = upPix->getTone(); + int p = upPix->getPaint(); + + if (t == 0xff && p == 0) + continue; + else { + int i = upPix->getInk(); + TPixel32 colorUp; + if (inksOnly) + switch (t) { + case 0: + colorUp = colors[i]; + CASE 255 : colorUp = TPixel::Transparent; + DEFAULT: + colorUp = antialias(colors[i], 255 - t); + } + else + switch (t) { + case 0: + colorUp = colors[i]; + CASE 255 : colorUp = colors[p]; + DEFAULT: + colorUp = blend(colors[i], colors[p], t, TPixelCM32::getMaxTone()); + } + + if (colorUp.m == 255) + *dnPix = colorUp; + else if (colorUp.m != 0) + *dnPix = quickOverPix(*dnPix, colorUp); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +// +// doQuickPutCmapped + transparencyCheck + inkCheck + paintcheck +// +//============================================================================= +/* +TPixel TransparencyCheckBlackBgInk = TPixel(255,255,255); //bg +TPixel TransparencyCheckWhiteBgInk = TPixel(0,0,0); //ink +TPixel TransparencyCheckPaint = TPixel(127,127,127); //paint*/ + +void doQuickPutCmapped( + const TRaster32P &dn, + const TRasterCM32P &up, + const TPaletteP &palette, + const TAffine &aff, + const TRop::CmappedQuickputSettings &s) +/*const TPixel32& globalColorScale, + bool inksOnly, + bool transparencyCheck, + bool blackBgCheck, + int inkIndex, + int paintIndex)*/ +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + const int PADN = 16; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + TAffine invAff = inv(aff); + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); + int deltaYL = tround(deltaYD * (1 << PADN)); + if ((deltaXL == 0) && (deltaYL == 0)) + return; + int lxPred = up->getLx() * (1 << PADN) - 1; + int lyPred = up->getLy() * (1 << PADN) - 1; + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + vector paints(palette->getStyleCount()); + vector inks(palette->getStyleCount()); + + if (s.m_transparencyCheck) + for (int i = 0; i < palette->getStyleCount(); i++) { + paints[i] = s.m_transpCheckPaint; + inks[i] = s.m_blackBgCheck ? s.m_transpCheckBg : s.m_transpCheckInk; + } + else if (s.m_globalColorScale == TPixel::Black) + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + else + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = applyColorScaleCMapped(palette->getStyle(i)->getAverageColor(), s.m_globalColorScale); + + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixelCM32 *upBasePix = up->pixels(); + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + TPointD a = invAff * TPointD(xMin, y); + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; + int kMinY = 0, kMaxY = xMax - xMin; + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } else { + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + if (deltaYL == 0) { + if ((yL0 < 0) || (lyPred < yL0)) + continue; + } else if (deltaYL > 0) { + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; + if (yL0 < 0) + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; + } else { + if (yL0 < 0) + continue; + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + int xL = xL0 + (kMin - 1) * deltaXL; + int yL = yL0 + (kMin - 1) * deltaYL; + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + int xI = xL >> PADN; + int yI = yL >> PADN; + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + TPixelCM32 *upPix = upBasePix + (yI * upWrap + xI); + int t = upPix->getTone(); + int p = upPix->getPaint(); + + if (t == 0xff && p == 0) + continue; + else { + int i = upPix->getInk(); + TPixel32 colorUp; + if (s.m_inksOnly) + switch (t) { + case 0: + colorUp = (i == s.m_inkIndex) ? TPixel::Red : inks[i]; + CASE 255 : colorUp = TPixel::Transparent; + DEFAULT: + TPixel inkColor; + if (i == s.m_inkIndex) { + inkColor = TPixel::Red; + if (p == 0) { + t = t / 2; //transparency check(for a bug!) darken semitrasparent pixels; ghibli likes it, and wants it also for ink checks... + //otherwise, ramps goes always from reds towards grey... + } + } else + inkColor = inks[i]; + + colorUp = antialias(inkColor, 255 - t); + } + else + switch (t) { + case 0: + colorUp = (i == s.m_inkIndex) ? TPixel::Red : inks[i]; + CASE 255 : colorUp = (p == s.m_paintIndex) ? TPixel::Red : paints[p]; + DEFAULT: + TPixel paintColor = (p == s.m_paintIndex) ? TPixel::Red : paints[p]; + TPixel inkColor; + if (i == s.m_inkIndex) { + inkColor = TPixel::Red; + if (p == 0) { + paintColor = TPixel::Transparent; + } + } else + inkColor = inks[i]; + + if (s.m_transparencyCheck) + t = t / 2; + + colorUp = blend(inkColor, paintColor, t, TPixelCM32::getMaxTone()); + } + if (colorUp.m == 255) + *dnPix = colorUp; + else if (colorUp.m != 0) + *dnPix = quickOverPix(*dnPix, colorUp); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +void doQuickPutCmapped( + const TRaster32P &dn, + const TRasterCM32P &up, + const TPaletteP &palette, + double sx, double sy, + double tx, double ty, + const TPixel32 &globalColorScale, + bool inksOnly) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine + // mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + // 0 <= xL0 + k*deltaXL < up->getLx()*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up+(bordo destro/basso) + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + + int count = tmax(palette->getStyleCount(), TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + vector paints(count, TPixel32::Red); + vector inks(count, TPixel32::Red); + if (globalColorScale != TPixel::Black) + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = applyColorScaleCMapped(palette->getStyle(i)->getAverageColor(), globalColorScale); + else + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + dn->lock(); + up->lock(); + TPixelCM32 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + + // inizializza yL + int yL = yL0 + (kMinY - 1) * deltaYL; + + // scorre le scanline di boundingBoxD + for (int kY = kMinY; kY <= kMaxY; kY++, dnRow += dnWrap) { + // inizializza xL + int xL = xL0 + (kMinX - 1) * deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + + TPixel32 *dnPix = dnRow + xMin + kMinX; + TPixel32 *dnEndPix = dnRow + xMin + kMaxX + 1; + + // scorre i pixel sulla (yMin + kY)-esima scanline di dn + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + // il punto di up TPointD(xL/(1<> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixelCM32 *upPix = upBasePix + (yI * upWrap + xI); + int t = upPix->getTone(); + int p = upPix->getPaint(); + assert(0 <= t && t < 256); + assert(0 <= p && p < (int)paints.size()); + + if (t == 0xff && p == 0) + continue; + else { + int i = upPix->getInk(); + assert(0 <= i && i < (int)inks.size()); + TPixel32 colorUp; + if (inksOnly) + switch (t) { + case 0: + colorUp = inks[i]; + CASE 255 : colorUp = TPixel::Transparent; + DEFAULT: + colorUp = antialias(inks[i], 255 - t); + } + else + switch (t) { + case 0: + colorUp = inks[i]; + CASE 255 : colorUp = paints[p]; + + DEFAULT: + colorUp = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + } + + if (colorUp.m == 255) + *dnPix = colorUp; + else if (colorUp.m != 0) + *dnPix = quickOverPix(*dnPix, colorUp); + } + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= +//============================================================================= +//============================================================================= + +void doQuickResampleColorFilter( + const TRaster32P &dn, + const TRasterCM32P &up, + const TPaletteP &plt, + const TAffine &aff, + UCHAR colorMask) +{ + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + const int PADN = 16; + + vector paints(plt->getStyleCount()); + vector inks(plt->getStyleCount()); + + for (int i = 0; i < plt->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(plt->getStyle(i)->getAverageColor()); + + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); // clipping y su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); // clipping x su dn + + TAffine invAff = inv(aff); // inversa di aff + + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = up->getLx() * (1 << PADN) - 1; // TINT32 predecessore di up->getLx() + int lyPred = up->getLy() * (1 << PADN) - 1; // TINT32 predecessore di up->getLy() + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixelCM32 *upBasePix = up->pixels(); + + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + TPointD a = invAff * TPointD(xMin, y); + int xL0 = tround((a.x + 0.5) * (1 << PADN)); + int yL0 = tround((a.y + 0.5) * (1 << PADN)); + int kMinX = 0, kMaxX = xMax - xMin; // clipping su dn + int kMinY = 0, kMaxY = xMax - xMin; // clipping su dn + if (deltaXL == 0) { + if ((xL0 < 0) || (lxPred < xL0)) + continue; + } else if (deltaXL > 0) { + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } else // (deltaXL < 0) + { + if (xL0 < 0) + continue; + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + if (deltaYL == 0) { + if ((yL0 < 0) || (lyPred < yL0)) + continue; + } else if (deltaYL > 0) { + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } else // (deltaYL < 0) + { + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + TPixel32 *dnPix = dnRow + xMin + kMin; + TPixel32 *dnEndPix = dnRow + xMin + kMax + 1; + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + int xI = xL >> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + TPixelCM32 *upPix = upBasePix + (yI * upWrap + xI); + int t = upPix->getTone(); + int p = upPix->getPaint(); + int i = upPix->getInk(); + TPixel32 colorUp; + switch (t) { + case 0: + colorUp = inks[i]; + CASE 255 : colorUp = paints[p]; + DEFAULT: + colorUp = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + } + + if (colorMask == TRop::MChan) + dnPix->r = dnPix->g = dnPix->b = colorUp.m; + else { + dnPix->r = ((colorMask & TRop::RChan) ? colorUp.r : 0); + dnPix->g = ((colorMask & TRop::GChan) ? colorUp.g : 0); + dnPix->b = ((colorMask & TRop::BChan) ? colorUp.b : 0); + } + dnPix->m = 255; + } + } + dn->unlock(); + up->unlock(); +} + +//========================================================== + +#endif //TNZCORE_LIGHT + +#ifdef OPTIMIZE_FOR_LP64 +void doQuickResampleFilter_optimized( + const TRaster32P &dn, + const TRaster32P &up, + const TAffine &aff) +{ + // se aff e' degenere la controimmagine di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + TAffine invAff = inv(aff); // inversa di aff + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate + // del pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + // naturale predecessore di up->getLx() - 1 + int lxPred = (up->getLx() - 2) * (1 << PADN); + + // naturale predecessore di up->getLy() - 1 + int lyPred = (up->getLy() - 2) * (1 << PADN); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *dnRow = dn->pixels(yMin); + TPixel32 *upBasePix = up->pixels(); + + long c1; + long c2; + + long c3; + long c4; + + long c5; + long c6; + + long s_rg; + long s_br; + long s_gb; + + UINT32 rColDownTmp; + UINT32 gColDownTmp; + UINT32 bColDownTmp; + + UINT32 rColUpTmp; + UINT32 gColUpTmp; + UINT32 bColUpTmp; + + register unsigned char rCol; + register unsigned char gCol; + register unsigned char bCol; + + int xI; + int yI; + + int xWeight1; + int xWeight0; + int yWeight1; + int yWeight0; + + TPixel32 *upPix00; + TPixel32 *upPix10; + TPixel32 *upPix01; + TPixel32 *upPix11; + + TPointD a; + int xL0; + int yL0; + int kMinX; + int kMaxX; + int kMinY; + int kMaxY; + + int kMin; + int kMax; + TPixel32 *dnPix; + TPixel32 *dnEndPix; + int xL; + int yL; + + int y = yMin; + ++yMax; + // scorre le scanline di boundingBoxD + for (; y < yMax - 32; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST_X_32 + } + for (; y < yMax - 16; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST_X_16 + } + for (; y < yMax - 8; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST_X_8 + } + for (; y < yMax - 4; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST_X_4 + } + for (; y < yMax - 2; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST_X_2 + } + for (; y < yMax; ++y, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_FIRST + } + dn->unlock(); + up->unlock(); +} +#endif + +//============================================================================= +//============================================================================= +//============================================================================= + +#ifdef OPTIMIZE_FOR_LP64 + +void doQuickResampleFilter_optimized( + const TRaster32P &dn, + const TRaster32P &up, + double sx, double sy, + double tx, double ty) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((sx == 0) || (sy == 0)) + return; + + // contatore bit di shift + const int PADN = 16; + + // maschera del filtro bilineare + const int MASKN = (1 << PADN) - 1; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TAffine aff(sx, 0, tx, 0, sy, ty); + TRectD boundingBoxD = TRectD(convert(dn->getSize())) * + (aff * TRectD(0, 0, up->getLx() - 2, up->getLy() - 2)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = tmax(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nello scorrere le scanline di boundingBoxD, il passaggio alla scanline + // successiva comporta l'incremento (0, deltaYD) delle coordinate dei + // pixels corrispondenti di up + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, 0) delle coordinate del + // pixel corrispondente di up + + double deltaXD = invAff.a11; + double deltaYD = invAff.a22; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' + // un segmento (o un punto) + if ((deltaXL == 0) || (deltaYL == 0)) + return; + + // (1) equazione (kX, kY)-parametrica di boundingBoxD: + // (xMin, yMin) + kX*(1, 0) + kY*(0, 1), + // kX = 0, ..., (xMax - xMin), + // kY = 0, ..., (yMax - yMin) + + // (2) equazione (kX, kY)-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, yMin) + kX*(deltaXD, 0) + kY*(0, deltaYD), + // kX = kMinX, ..., kMaxX + // con 0 <= kMinX <= kMaxX <= (xMax - xMin) + // + // kY = kMinY, ..., kMaxY + // con 0 <= kMinY <= kMaxY <= (yMax - yMin) + + // calcola kMinX, kMaxX, kMinY, kMaxY intersecando la (2) con i lati di up + + // il segmento [a, b] di up (con gli estremi eventualmente invertiti) e' + // la controimmagine mediante aff della porzione di scanline + // [ (xMin, yMin), (xMax, yMin) ] di dn + + // TPointD b = invAff*TPointD(xMax, yMin); + TPointD a = invAff * TPointD(xMin, yMin); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + kX*deltaXL <= (up->getLx() - 2)*(1<getLy() - 2)*(1<getLx() - 1) + int lxPred = (up->getLx() - 2) * (1 << PADN); + + // TINT32 predecessore di (up->getLy() - 1) + int lyPred = (up->getLy() - 2) * (1 << PADN); + + // 0 <= xL0 + k*deltaXL <= (up->getLx() - 2)*(1< + // 0 <= xL0 + k*deltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL <= (up->getLy() - 2)*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinY, kMaxY intersecando la (2) con i lati + // (y = yMin) e (y = yMax) di up + if (deltaYL > 0) // (deltaYL != 0) + { + // [a, b] interno ad up contratto + assert(yL0 <= lyPred); + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= yL0); + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + // calcola kMinY, kMaxY effettuando anche il clippind su dn + kMinY = tmax(kMinY, (int)0); + kMaxY = tmin(kMaxY, yMax - yMin); + + // calcola kMinX, kMaxX intersecando la (2) con i lati + // (x = xMin) e (x = xMax) di up + if (deltaXL > 0) // (deltaXL != 0) + { + // [a, b] interno ad up contratto + assert(xL0 <= lxPred); + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] interno ad up contratto + assert(0 <= xL0); + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + // calcola kMinX, kMaxX effettuando anche il clippind su dn + kMinX = tmax(kMinX, (int)0); + kMaxX = tmin(kMaxX, xMax - xMin); + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel32 *upBasePix = up->pixels(); + TPixel32 *dnRow = dn->pixels(yMin + kMinY); + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" + // del pixel corrente di up + int yL = yL0 + (kMinY - 1) * deltaYL; // inizializza yL + + long c1; + long c2; + + long c3; + long c4; + + long c5; + long c6; + + long s_rg; + long s_br; + long s_gb; + + UINT32 rColDownTmp; + UINT32 gColDownTmp; + UINT32 bColDownTmp; + + UINT32 rColUpTmp; + UINT32 gColUpTmp; + UINT32 bColUpTmp; + + int xI; + TPixel32 *upPix00; + TPixel32 *upPix10; + TPixel32 *upPix01; + TPixel32 *upPix11; + int xWeight1; + int xWeight0; + + register unsigned char rCol; + register unsigned char gCol; + register unsigned char bCol; + + int xL; + int yI; + int yWeight1; + int yWeight0; + TPixel32 *dnPix; + TPixel32 *dnEndPix; + int kY = kMinY; + ++kMaxY; + + // scorre le scanline di boundingBoxD + for (; kY < kMaxY - 32; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND_X_32 + } + for (; kY < kMaxY - 16; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND_X_16 + } + for (; kY < kMaxY - 8; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND_X_8 + } + for (; kY < kMaxY - 4; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND_X_4 + } + for (; kY < kMaxY - 2; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND_X_2 + } + for (; kY < kMaxY; kY++, dnRow += dnWrap) { + EXTERNAL_LOOP_THE_SECOND + } + dn->unlock(); + up->unlock(); +} +#endif + +// namespace +}; + +#ifndef TNZCORE_LIGHT +//============================================================================= +// +// quickPut (paletted) +// +//============================================================================= + +void TRop::quickPut(const TRasterP &dn, + const TRasterCM32P &upCM32, const TPaletteP &plt, + const TAffine &aff, const TPixel32 &globalColorScale, bool inksOnly) +{ + TRaster32P dn32 = dn; + if (dn32 && upCM32) + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickPutCmapped(dn32, upCM32, plt, aff.a11, aff.a22, aff.a13, aff.a23, globalColorScale, inksOnly); + else + doQuickPutCmapped(dn32, upCM32, plt, aff, globalColorScale, inksOnly); + else + throw TRopException("raster type mismatch"); +} + +//============================================================================= +// +// quickPut (paletted + transparency check + ink check + paint check) +// +//============================================================================= + +void TRop::quickPut(const TRasterP &dn, + const TRasterCM32P &upCM32, const TPaletteP &plt, const TAffine &aff, + const CmappedQuickputSettings &settings) //const TPixel32& globalColorScale, bool inksOnly, bool transparencyCheck, bool blackBgCheck, int inkIndex, int paintIndex) +{ + TRaster32P dn32 = dn; + if (dn32 && upCM32) + doQuickPutCmapped(dn32, upCM32, plt, aff, settings); //globalColorScale, inksOnly, transparencyCheck, blackBgCheck, inkIndex, paintIndex); + else + throw TRopException("raster type mismatch"); +} + +void TRop::quickResampleColorFilter( + const TRasterP &dn, + const TRasterP &up, + const TAffine &aff, + const TPaletteP &plt, + UCHAR colorMask) +{ + + TRaster32P dn32 = dn; + TRaster32P up32 = up; + TRaster64P up64 = up; + TRasterCM32P upCM32 = up; + if (dn32 && up32) + doQuickResampleColorFilter(dn32, up32, aff, colorMask); + else if (dn32 && upCM32) + doQuickResampleColorFilter(dn32, upCM32, plt, aff, colorMask); + else if (dn32 && up64) + doQuickResampleColorFilter(dn32, up64, aff, colorMask); + //else if (dn32 && upCM32) + // doQuickResampleColorFilter(dn32, upCM32, aff, plt, colorMask); + else + throw TRopException("raster type mismatch"); +} + +#endif //TNZCORE_LIGHT + +//============================================================================= +//============================================================================= +// +// quickPut (Bilinear/Closest) +// +//============================================================================= + +void quickPut( + const TRasterP &dn, + const TRasterP &up, + const TAffine &aff, + TRop::ResampleFilterType filterType, + const TPixel32 &colorScale, + bool doPremultiply, bool whiteTransp, bool firstColumn, + bool doRasterDarkenBlendedView) +{ + assert(filterType == TRop::Bilinear || + filterType == TRop::ClosestPixel); + + bool bilinear = filterType == TRop::Bilinear; + + TRaster32P dn32 = dn; + TRaster32P up32 = up; + TRasterGR8P dn8 = dn; + TRasterGR8P up8 = up; + TRaster64P up64 = up; + + if (up8 && dn32) { + assert(filterType == TRop::ClosestPixel); + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickPutNoFilter(dn32, up8, aff.a11, aff.a22, aff.a13, aff.a23, colorScale); + else + doQuickPutNoFilter(dn32, up8, aff, colorScale); + } else if (dn32 && up32) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + if (bilinear) + doQuickPutFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickPutNoFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23, colorScale, doPremultiply, whiteTransp, firstColumn, doRasterDarkenBlendedView); + else if (bilinear) + doQuickPutFilter(dn32, up32, aff); + else + doQuickPutNoFilter(dn32, up32, aff, colorScale, doPremultiply, whiteTransp, firstColumn, doRasterDarkenBlendedView); + } else if (dn32 && up64) + doQuickPutNoFilter(dn32, up64, aff, doPremultiply, firstColumn); + else + throw TRopException("raster type mismatch"); +} + +//============================================================================= +template +void doQuickResampleNoFilter( + const TRasterPT &dn, + const TRasterPT &up, + const TAffine &aff) +{ + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) + return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(tmax(up->getLx(), up->getLy()) < (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + int yMin = tmax(tfloor(boundingBoxD.y0), 0); // clipping y su dn + int yMax = tmin(tceil(boundingBoxD.y1), dn->getLy() - 1); // clipping y su dn + int xMin = tmax(tfloor(boundingBoxD.x0), 0); // clipping x su dn + int xMax = tmin(tceil(boundingBoxD.x1), dn->getLx() - 1); // clipping x su dn + + TAffine invAff = inv(aff); // inversa di aff + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate + // del pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + int deltaXL = tround(deltaXD * (1 << PADN)); // deltaXD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); // deltaYD "TLonghizzato" (round) + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' + // un segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) + return; + + int lxPred = up->getLx() * (1 << PADN) - 1; // TINT32 predecessore di up->getLx() + int lyPred = up->getLy() * (1 << PADN) - 1; // TINT32 predecessore di up->getLy() + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + PIX *dnRow = dn->pixels(yMin); + PIX *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), + // k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax + // con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente intersecando + // la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*eltaXL <= lxPred + + // 0 <= yL0 + k*deltaYL < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) + continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) + continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) + continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) + continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) + continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) + continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = tmax(kMinX, kMinY, (int)0); + int kMax = tmin(kMaxX, kMaxY, xMax - xMin); + + PIX *dnPix = dnRow + xMin + kMin; + PIX *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && + (0 <= yI) && (yI <= up->getLy() - 1)); + + *dnPix = *(upBasePix + (yI * upWrap + xI)); + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= + +#ifdef OPTIMIZE_FOR_LP64 + +void quickResample_optimized( + const TRasterP &dn, + const TRasterP &up, + const TAffine &aff, + TRop::ResampleFilterType filterType) +{ + assert(filterType == TRop::Bilinear || + filterType == TRop::ClosestPixel); + + bool bilinear = filterType == TRop::Bilinear; + + TRaster32P dn32 = dn; + TRaster32P up32 = up; + TRasterGR8P dn8 = dn; + TRasterGR8P up8 = up; + if (dn32 && up32) + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + if (bilinear) + doQuickResampleFilter_optimized(dn32, up32, + aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleNoFilter(dn32, up32, aff.a11, aff.a22, aff.a13, + aff.a23); + else if (bilinear) + doQuickResampleFilter_optimized(dn32, up32, aff); + else + doQuickResampleNoFilter(dn32, up32, aff); + else + throw TRopException("raster type mismatch"); +} + +#endif + +//============================================================================= + +void quickResample( + const TRasterP &dn, + const TRasterP &up, + const TAffine &aff, + TRop::ResampleFilterType filterType) +{ + +#ifdef OPTIMIZE_FOR_LP64 + + quickResample_optimized(dn, up, aff, filterType); + +#else + + assert(filterType == TRop::Bilinear || + filterType == TRop::ClosestPixel); + + bool bilinear = filterType == TRop::Bilinear; + + TRaster32P dn32 = dn; + TRaster32P up32 = up; + TRasterCM32P dnCM32 = dn; + TRasterCM32P upCM32 = up; + TRasterGR8P dn8 = dn; + TRasterGR8P up8 = up; + dn->clear(); + + if (bilinear) { + if (dn32 && up32) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickResampleFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleFilter(dn32, up32, aff); + } else if (dn32 && up8) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickResampleFilter(dn32, up8, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleFilter(dn32, up8, aff); + } else + throw TRopException("raster type mismatch"); + } else { + if (dn32 && up32) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickResampleNoFilter(dn32, up32, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleNoFilter(dn32, up32, aff); + + } else if (dnCM32 && upCM32) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickResampleNoFilter(dnCM32, upCM32, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleNoFilter(dnCM32, upCM32, aff); + } else if (dn8 && up8) { + if (areAlmostEqual(aff.a12, 0) && areAlmostEqual(aff.a21, 0)) + doQuickResampleNoFilter(dn8, up8, aff.a11, aff.a22, aff.a13, aff.a23); + else + doQuickResampleNoFilter(dn8, up8, aff); + } else + throw TRopException("raster type mismatch"); + } + +#endif +} + +void quickPut(const TRasterP &out, const TRasterCM32P &up, const TAffine &aff, + const TPixel32 &inkCol, const TPixel32 &paintCol); diff --git a/toonz/sources/common/trop/quickputP.h b/toonz/sources/common/trop/quickputP.h new file mode 100644 index 0000000..01b0d98 --- /dev/null +++ b/toonz/sources/common/trop/quickputP.h @@ -0,0 +1,26 @@ + + +#ifndef QUICKPUT_P_INCLUDED +#define QUICKPUT_P_INCLUDED + +#include "trop.h" + +void quickPut(const TRasterP &dn, const TRasterP &up, + const TAffine &aff, TRop::ResampleFilterType filterType, + const TPixel32 &colorScale = TPixel32::Black, bool doPremultiply = false, bool whiteTransp = false, bool firstColumn = false, + bool doRasterDarkenBlendedView = false); + +void quickResample(const TRasterP &dn, const TRasterP &up, + const TAffine &aff, TRop::ResampleFilterType filterType); + +void quickPutCmapped(const TRasterP &out, + const TRasterCM32P &up, const TPaletteP &plt, + const TAffine &aff); + +#ifdef __LP64__ +void quickResample_optimized(const TRasterP &dn, const TRasterP &up, + const TAffine &aff, + TRop::ResampleFilterType filterType); +#endif + +#endif diff --git a/toonz/sources/common/trop/raster_edge_evaluator.h b/toonz/sources/common/trop/raster_edge_evaluator.h new file mode 100644 index 0000000..55250cc --- /dev/null +++ b/toonz/sources/common/trop/raster_edge_evaluator.h @@ -0,0 +1,45 @@ + + +#ifndef RASTER_EDGE_EVALUATOR_H +#define RASTER_EDGE_EVALUATOR_H + +#include "tcg/tcg_sequence_ops.h" +#include "tcg/tcg_polylineops.h" + +//************************************************************************* +// Standard Raster Edge Evaluator +//************************************************************************* + +/*! + \brief This class implements an evaluator for tcg's sequential reduction algorithm + that can be used to simplify the borders extracted from a raster image under + a specified tolerance factor. This is typically used as a step in polygonal-based + image vectorization processes. + +\sa The tcg::sequence_ops::minimalPath() function. +*/ +template +class RasterEdgeEvaluator : public tcg::polyline_ops::StandardDeviationEvaluator +{ + double m_tolerance; //!< Maximal distance of an edge from one of the + //!< points it approximates, in the Manhattan metric + double m_maxLength; //!< Maximal length of an acceptable edge length, + //!< in the standard metric +public: + typedef typename tcg::polyline_ops::StandardDeviationEvaluator::iterator_type iterator_type; + typedef typename tcg::polyline_ops::StandardDeviationEvaluator::point_type point_type; + typedef typename tcg::polyline_ops::StandardDeviationEvaluator::penalty_type penalty_type; + +public: + RasterEdgeEvaluator(const iterator_type &begin, const iterator_type &end, + double tolerance, double maxLength); + + iterator_type furthestFrom(const iterator_type &it); + penalty_type penalty(const iterator_type &a, const iterator_type &b); +}; + +#endif //RASTER_EDGE_EVALUATOR_H + +#ifdef INCLUDE_HPP +#include "raster_edge_evaluator.hpp" +#endif diff --git a/toonz/sources/common/trop/raster_edge_evaluator.hpp b/toonz/sources/common/trop/raster_edge_evaluator.hpp new file mode 100644 index 0000000..25be9b6 --- /dev/null +++ b/toonz/sources/common/trop/raster_edge_evaluator.hpp @@ -0,0 +1,116 @@ + +#ifndef RASTER_EDGE_EVALUATOR_HPP +#define RASTER_EDGE_EVALUATOR_HPP + +#include "raster_edge_evaluator.h" + +//******************************************************************************* +// Raster Edge Evaluator implementation +//******************************************************************************* + +template +RasterEdgeEvaluator::RasterEdgeEvaluator( + const iterator_type &begin, const iterator_type &end, + double tolerance, double maxLength) + : tcg::polyline_ops::StandardDeviationEvaluator(begin, end), m_tolerance(tolerance), m_maxLength(maxLength) +{ +} + +//-------------------------------------------------------------------------- + +template +typename RasterEdgeEvaluator::penalty_type +RasterEdgeEvaluator::penalty(const iterator_type &a, const iterator_type &b) +{ + return tcg::point_ops::norm(*b - *a) * tcg::polyline_ops::StandardDeviationEvaluator::penalty(a, b); +} + +//-------------------------------------------------------------------------- + +template +typename RasterEdgeEvaluator::iterator_type +RasterEdgeEvaluator::furthestFrom(const iterator_type &start) +{ + //Build the furthest possible forward difference for every vertex between begin and end. + point_type displace, oldDisplace; + point_type leftConstraint, rightConstraint; + point_type newLeftConstraint, newRightConstraint; + point_type leftDirConstr, rightDirConstr, dir, oldDir; + iterator_type it = start, jt; + + const double sqMaxLength = sq(m_maxLength); + + //Initialize search + leftConstraint = rightConstraint = point_type(); + leftDirConstr = rightDirConstr = point_type(); + oldDir = oldDisplace = point_type(); + + if (it != this->m_begin) + --it; //Chop left + + jt = it; + for (++jt; jt != this->m_end; ++jt) { + //Retrieve displacement from *it + displace = point_type(jt->x - it->x, jt->y - it->y); + dir = point_type(displace.x - oldDisplace.x, displace.y - oldDisplace.y); + + //Max length + if (oldDir.x != 0 || oldDir.y != 0) { + if (sq(displace.x) + sq(displace.y) > sqMaxLength) + break; + } else + leftDirConstr = rightDirConstr = dir; + + //Test displacement against the oldDisplacement. If it's reversing the + //direction, make it invalid. + + if (cross(oldDir, dir) > 0) + leftDirConstr = dir; + + if (cross(oldDir, dir) < 0) + rightDirConstr = dir; + + //Test constraints + + /*if(cross(rightDirConstr, leftDirConstr) <= 0 && + leftDirConstr * rightDirConstr < 0) + break;*/ + if (cross(rightDirConstr, leftDirConstr) < 0) + break; + + if (cross(displace, leftConstraint) < 0) + break; + if (cross(displace, rightConstraint) > 0) + break; + + if (tmax(displace.x, -displace.x, displace.y, -displace.y) > m_tolerance) { + //Update m_tolerance constraints + newLeftConstraint.x = displace.x + + (displace.y < 0 || (displace.y == 0 && displace.x < 0) ? m_tolerance : -m_tolerance); + newLeftConstraint.y = displace.y + + (displace.x > 0 || (displace.x == 0 && displace.y < 0) ? m_tolerance : -m_tolerance); + + if (cross(newLeftConstraint, leftConstraint) >= 0) + leftConstraint = newLeftConstraint; + + newRightConstraint.x = displace.x + + (displace.y > 0 || (displace.y == 0 && displace.x < 0) ? m_tolerance : -m_tolerance); + + newRightConstraint.y = displace.y + + (displace.x < 0 || (displace.x == 0 && displace.y < 0) ? m_tolerance : -m_tolerance); + + if (cross(newRightConstraint, rightConstraint) <= 0) + rightConstraint = newRightConstraint; + } + + oldDisplace = displace; + oldDir = dir; + } + + if (jt != this->m_end) + --jt; //Chop Right + + return start + tmax((int)tmin(jt - start - 1, this->m_end - this->m_begin - 2), 1); +} + +#endif // RASTER_EDGE_EVALUATOR_HPP diff --git a/toonz/sources/common/trop/raster_edge_iterator.h b/toonz/sources/common/trop/raster_edge_iterator.h new file mode 100644 index 0000000..a714991 --- /dev/null +++ b/toonz/sources/common/trop/raster_edge_iterator.h @@ -0,0 +1,117 @@ + + +#ifndef RASTER_EDGE_ITERATOR_H +#define RASTER_EDGE_ITERATOR_H + +#include "traster.h" + +namespace TRop +{ +namespace borders +{ + +//********************************************************************************************************* +// RasterEdgeIterator class +//********************************************************************************************************* + +/*! + The RasterEdgeIterator class models a forward iterator traversing a border of a + raster image. +*/ +template +class RasterEdgeIterator +{ +public: + typedef PixelSelector selector_type; + typedef typename PixelSelector::pixel_type pixel_type; + typedef typename PixelSelector::value_type value_type; + typedef TRasterT raster_type; + typedef TRasterPT raster_typeP; + + enum { STRAIGHT = 0x0, + LEFT = 0x1, + RIGHT = 0x2, + AMBIGUOUS = 0x4, + AMBIGUOUS_LEFT = LEFT | AMBIGUOUS, + AMBIGUOUS_RIGHT = RIGHT | AMBIGUOUS, + UNKNOWN = 0x8 }; + +private: + raster_typeP m_ras; + selector_type m_selector; + + int m_lx_1, m_ly_1, m_wrap; + + value_type m_leftColor, m_rightColor, m_elbowColor; + pixel_type *m_leftPix, *m_rightPix; + + bool m_rightSide; + int m_turn; + + TPoint m_pos, m_dir; + +public: + RasterEdgeIterator(const raster_typeP &rin, const selector_type &selector, + const TPoint &pos, const TPoint &dir, int adherence = RIGHT); + + void setEdge(const TPoint &pos, const TPoint &dir); + + const raster_typeP &raster() const { return m_ras; } + const selector_type &selector() const { return m_selector; } + + const TPoint &pos() const { return m_pos; } + const TPoint &dir() const { return m_dir; } + + const value_type &leftColor() const { return m_leftColor; } + const value_type &rightColor() const { return m_rightColor; } + + const value_type &color() const { return m_rightSide ? m_rightColor : m_leftColor; } + const value_type &otherColor() const { return m_rightSide ? m_leftColor : m_rightColor; } + const value_type &elbowColor() const { return m_elbowColor; } + + pixel_type *leftPix() const { return m_leftPix; } + pixel_type *rightPix() const { return m_rightPix; } + + pixel_type *pix() const { return m_rightSide ? m_rightPix : m_leftPix; } + pixel_type *otherPix() const { return m_rightSide ? m_leftPix : m_rightPix; } + + int turn() const { return m_turn; } + + void setAdherence(int side) { m_rightSide = (side == RIGHT); } + int adherence() const { return m_rightSide ? RIGHT : LEFT; } + + RasterEdgeIterator &operator++(); + + bool operator==(const RasterEdgeIterator &it) const { return m_pos == it.m_pos && m_dir == it.m_dir; } + bool operator!=(const RasterEdgeIterator &it) const { return !operator==(it); } + +private: + void pixels(pixel_type *&pixLeft, pixel_type *&pixRight); + void colors(value_type &leftColor, value_type &rightColor); + void turn(const value_type &newLeftColor, const value_type &newRightColor); + void turnLeft() + { + int temp = m_dir.x; + m_dir.x = -m_dir.y; + m_dir.y = temp; + m_turn = LEFT; + } + void turnRight() + { + int temp = m_dir.x; + m_dir.x = m_dir.y; + m_dir.y = -temp; + m_turn = RIGHT; + } + void turnAmbiguous(const value_type &newLeftColor, const value_type &newRightColor); +}; +} +} // namespace TRop::borders + +#endif //RASTER_EDGE_ITERATOR_H + +//===================================================================================== + +#ifdef INCLUDE_HPP +#include "raster_edge_iterator.hpp" +#endif //INCLUDE_HPP diff --git a/toonz/sources/common/trop/raster_edge_iterator.hpp b/toonz/sources/common/trop/raster_edge_iterator.hpp new file mode 100644 index 0000000..c5f3121 --- /dev/null +++ b/toonz/sources/common/trop/raster_edge_iterator.hpp @@ -0,0 +1,246 @@ + +#ifndef RASTER_EDGE_ITERATOR_HPP +#define RASTER_EDGE_ITERATOR_HPP + +#include "raster_edge_iterator.h" + +namespace TRop +{ +namespace borders +{ + +template +RasterEdgeIterator::RasterEdgeIterator( + const raster_typeP &rin, const selector_type &selector, + const TPoint &pos, const TPoint &dir, int adherence) + : m_ras(rin), m_lx_1(rin->getLx() - 1), m_ly_1(rin->getLy() - 1), m_wrap(rin->getWrap()), m_selector(selector), m_pos(pos), m_dir(dir), m_elbowColor(selector.transparent()), m_rightSide(adherence == RIGHT), m_turn(UNKNOWN) +{ + pixels(m_leftPix, m_rightPix); + colors(m_leftColor, m_rightColor); +} + +//--------------------------------------------------------------------------------------------- + +template +void RasterEdgeIterator::setEdge(const TPoint &pos, const TPoint &dir) +{ + m_pos = pos, m_dir = dir; + pixels(m_leftPix, m_rightPix); + colors(m_leftColor, m_rightColor); +} + +//--------------------------------------------------------------------------------------------- + +template +inline void RasterEdgeIterator::pixels( + pixel_type *&pixLeft, pixel_type *&pixRight) +{ + pixel_type *pix = m_ras->pixels(0) + m_pos.y * m_wrap + m_pos.x; + if (m_dir.y) + if (m_dir.y > 0) + pixLeft = pix - 1, pixRight = pix; + else + pixLeft = pix - m_wrap, pixRight = pixLeft - 1; + else if (m_dir.x > 0) + pixLeft = pix, pixRight = pix - m_wrap; + else + pixRight = pix - 1, pixLeft = pixRight - m_wrap; +} + +//--------------------------------------------------------------------------------------------- + +template +inline void RasterEdgeIterator::colors( + value_type &leftColor, value_type &rightColor) +{ + if (m_dir.y) + if (m_dir.y > 0) { + if (m_pos.y > m_ly_1) + leftColor = rightColor = m_selector.transparent(); + else { + leftColor = (m_pos.x > 0) ? m_selector.value(*m_leftPix) : m_selector.transparent(); + rightColor = (m_pos.x <= m_lx_1) ? m_selector.value(*m_rightPix) : m_selector.transparent(); + } + } else { + if (m_pos.y < 1) + leftColor = rightColor = m_selector.transparent(); + else { + leftColor = (m_pos.x <= m_lx_1) ? m_selector.value(*m_leftPix) : m_selector.transparent(); + rightColor = (m_pos.x > 0) ? m_selector.value(*m_rightPix) : m_selector.transparent(); + } + } + else if (m_dir.x > 0) { + if (m_pos.x > m_lx_1) + leftColor = rightColor = m_selector.transparent(); + else { + leftColor = (m_pos.y <= m_ly_1) ? m_selector.value(*m_leftPix) : m_selector.transparent(); + rightColor = (m_pos.y > 0) ? m_selector.value(*m_rightPix) : m_selector.transparent(); + } + } else { + if (m_pos.x < 1) + leftColor = rightColor = m_selector.transparent(); + else { + leftColor = (m_pos.y > 0) ? m_selector.value(*m_leftPix) : m_selector.transparent(); + rightColor = (m_pos.y <= m_ly_1) ? m_selector.value(*m_rightPix) : m_selector.transparent(); + } + } +} + +//--------------------------------------------------------------------------------------------- + +template +inline void RasterEdgeIterator::turn(const value_type &newLeftColor, const value_type &newRightColor) +{ + if (m_rightSide) { + if (newLeftColor == m_rightColor) { + if (newRightColor == m_leftColor) + turnAmbiguous(newLeftColor, newRightColor); + else + turnLeft(); + } else { + if (newRightColor != m_rightColor) + turnRight(); + else + m_turn = STRAIGHT; + } + + m_elbowColor = newLeftColor; + } else { + if (newRightColor == m_leftColor) { + if (newLeftColor == m_rightColor) + turnAmbiguous(newLeftColor, newRightColor); + else + turnRight(); + } else { + if (newLeftColor != m_leftColor) + turnLeft(); + else + m_turn = STRAIGHT; + } + + m_elbowColor = newRightColor; + } + + pixels(m_leftPix, m_rightPix); +} + +//--------------------------------------------------------------------------------------------- + +template +inline void RasterEdgeIterator::turnAmbiguous( + const value_type &newLeftColor, const value_type &newRightColor) +{ + pixel_type *pix = m_ras->pixels(0) + m_pos.y * m_wrap + m_pos.x; + UCHAR count1 = 0, count2 = 0; + + value_type val; + + // Check the 4x4 neighbourhood and connect the minority color + if (m_pos.x > 2) { + val = m_selector.value(*(pix - 2)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + + val = m_selector.value(*(pix - 2 - m_wrap)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + } + + if (m_pos.x < m_lx_1) { + val = m_selector.value(*(pix + 1)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + + val = m_selector.value(*(pix + 1 - m_wrap)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + } + + if (m_pos.y > 2) { + int wrap2 = m_wrap << 1; + + val = m_selector.value(*(pix - wrap2)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + + val = m_selector.value(*(pix - wrap2 - 1)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + } + + if (m_pos.y < m_ly_1) { + val = m_selector.value(*(pix + m_wrap)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + + val = m_selector.value(*(pix + m_wrap - 1)); + if (val == m_leftColor) + ++count1; + else if (val == m_rightColor) + ++count2; + } + + // Minority connection - join the one with less count + if (count1 < count2) + turnRight(); // Join m_leftColor == newRightColor + else if (count1 > count2) + turnLeft(); // Join m_rightColor == newLeftColor + else if (m_rightColor < m_leftColor) + turnLeft(); + else + turnRight(); + + m_turn |= AMBIGUOUS; +} + +//--------------------------------------------------------------------------------------------- + +template +RasterEdgeIterator &RasterEdgeIterator::operator++() +{ + value_type newLeftColor = m_leftColor, newRightColor = m_rightColor; + + int pixAdd = m_dir.y * m_wrap + m_dir.x; + if (m_rightSide) { + do { + m_pos.x += m_dir.x, m_pos.y += m_dir.y; + m_leftPix += pixAdd, m_rightPix += pixAdd; + m_leftColor = newLeftColor; + + colors(newLeftColor, newRightColor); + } while ((newRightColor == m_rightColor) && (newLeftColor != newRightColor) && + m_selector.skip(m_leftColor, newLeftColor)); + } else { + do { + m_pos.x += m_dir.x, m_pos.y += m_dir.y; + m_leftPix += pixAdd, m_rightPix += pixAdd; + m_rightColor = newRightColor; + + colors(newLeftColor, newRightColor); + } while ((newLeftColor == m_leftColor) && (newLeftColor != newRightColor) && + m_selector.skip(m_rightColor, newRightColor)); + } + + turn(newLeftColor, newRightColor); + colors(m_leftColor, m_rightColor); + + return *this; +} +} +} // namespace TRop::borders + +#endif // RASTER_EDGE_ITERATOR_HPP diff --git a/toonz/sources/common/trop/runsmap.cpp b/toonz/sources/common/trop/runsmap.cpp new file mode 100644 index 0000000..2d8d415 --- /dev/null +++ b/toonz/sources/common/trop/runsmap.cpp @@ -0,0 +1,47 @@ + + +#include "runsmap.h" + +//--------------------------------------------------------------------------------------------- + +void RunsMap::setRunLength(TPixelGR8 *pix, TUINT32 length) +{ + TPixelGR8 *origPix = pix; + pix = origPix; + + TPixelGR8 *pixRev = pix + length - 1; + + --length; + if (length < 3) + pix->value = pixRev->value = (length << 6); + else { + pix->value = pixRev->value = (3 << 6); + ++pix, --pixRev; + if (length < 255) + pix->value = pixRev->value = length; + else { + pix->value = pixRev->value = 255; + ++pix, pixRev -= 4; + TUINT32 *l = (TUINT32 *)pix; + *l = length; + } + } + + assert(runLength(origPix) == (length + 1)); +} + +//--------------------------------------------------------------------------------------------- + +TUINT32 RunsMap::runLength(const TPixelGR8 *pix, bool reversed) const +{ + int length = pix->value >> 6; + if (length >= 3) { + pix += (reversed) ? -1 : 1, length = pix->value; + if (length >= 255) { + pix += (reversed) ? -4 : 1; + length = *(TUINT32 *)pix; + } + } + + return length + 1; +} diff --git a/toonz/sources/common/trop/runsmap.h b/toonz/sources/common/trop/runsmap.h new file mode 100644 index 0000000..04e057a --- /dev/null +++ b/toonz/sources/common/trop/runsmap.h @@ -0,0 +1,90 @@ + + +#ifndef RUNSMAP_H +#define RUNSMAP_H + +#include "traster.h" + +//********************************************************************************************************* +// Run Maps +//********************************************************************************************************* + +/*! + The RunsMapP is an auxiliary raster greymap type used to store run-length informations + about an image. + + Not every image pixel has valid informations. Only pixels corresponding to + run headers do, and those include the position of the next run header. + This means that users must always iterate from the beginning of a line to get valid + data. + + The following coding is adopted to extract the run length from the run headers: + + \li We'll use the last 2 bits only in the headers. With these, we can cover directly + those runs up to 4 pixels length. + \li When the length >=4, we require that one byte is taken in the run to store the length + up to 256 pixels. + \li When the length >= 256, we take 4 additional bytes to store the length (which this time + could go up to billions). + + Observe that the runsmap supports a symmetrical representation, useful + to traverse runs both forward and backwards. This means that 2 headers are + provided for each run, at the opposite ends (unless the run is 1 pixel-width). +*/ +class RunsMap : public TRasterT +{ +public: + RunsMap(int lx, int ly) : TRasterT(lx, ly) { clear(); } + + const UCHAR &runHeader(int x, int y) const { return pixels(y)[x].value; } + UCHAR &runHeader(int x, int y) { return pixels(y)[x].value; } + + TUINT32 runLength(const TPixelGR8 *run, bool reversed = false) const; + TUINT32 runLength(int x, int y, bool reversed = false) const + { + return runLength(pixels(y) + x, reversed); + } + +public: + void setRunLength(TPixelGR8 *run, TUINT32 length); + void setRunLength(int x, int y, TUINT32 length) { setRunLength(pixels(y) + x, length); } +}; + +//--------------------------------------------------------------------------------------------- + +#ifdef WIN32 +template class DV_EXPORT_API TSmartPointerT; +#endif + +class RunsMapP : public TSmartPointerT +{ +public: + RunsMapP() {} + RunsMapP(int lx, int ly) : TSmartPointerT(new RunsMap(lx, ly)) {} + RunsMapP(const TDimension &d) : TSmartPointerT(new RunsMap(d.lx, d.ly)) {} +}; + +//--------------------------------------------------------------------------------------------- + +template +void buildRunsMap(RunsMapP &runsMap, const TRasterPT &ras, const PixelSelector &selector) +{ + //Traverse the raster, extracting run lengths + int y, ly = ras->getLy(); + for (y = 0; y < ly; ++y) { + Pixel *lineStart = (Pixel *)ras->pixels(y), *lineEnd = lineStart + ras->getLx(); + + Pixel *pix, *runStart; + typename PixelSelector::value_type colorIndex; + for (pix = runStart = lineStart, colorIndex = selector.value(*pix); + pix < lineEnd; ++pix) + if (selector.value(*pix) != colorIndex) { + runsMap->setRunLength(runStart - lineStart, y, pix - runStart); + runStart = pix; + colorIndex = selector.value(*pix); + } + runsMap->setRunLength(runStart - lineStart, y, pix - runStart); + } +} + +#endif //RUNSMAP_H diff --git a/toonz/sources/common/trop/tantialias.cpp b/toonz/sources/common/trop/tantialias.cpp new file mode 100644 index 0000000..47cb38f --- /dev/null +++ b/toonz/sources/common/trop/tantialias.cpp @@ -0,0 +1,402 @@ + + +#include "trop.h" + +/* +See Alexander Reshetov's "Morphological Antialiasing" paper on Intel Labs site. + +Basically, this antialiasing algorithm is based on the following ideas: + + - Suppose that our image is just made up of flat colors. Then, a simple antialiasing + approach is that of assuming that the 'actual' line separating two distinct colors + is the polyline that passes through the midpoint of each edge of its original jaggy + counterpart. + As pixels around the border are cut through by the polyline, the area of the pixel + that is filled of a certain color is its weight in the output filtered pixel. + + - The above method can be applied on each single uniform piece of a scanline, considering + the lines originated by the vertical extensions of its left and right edges. + + - Of these lines, only those which lie completely on pixels adjacent to the edge are + considered - so that the antialiasing effect is kept only around the contours. + +This algorithm would yield a good result at what may be considered 50% softness. Implementing +a generalized softness simply requires that the line slopes used above are modified +accordingly (divide by 2 * softFactor). +*/ + +//----------------------------------------------------------------------------------------- + +namespace +{ +template +class PixelSelector +{ +public: + typedef PIX pixel_type; + +private: + int m_thresh; + +public: + PixelSelector(int thresh) : m_thresh(thresh) {} + + bool areEqual(const PIX &a, const PIX &b) const + { + return tmax(abs((int)a.r - b.r), + abs((int)a.g - b.g), + abs((int)a.b - b.b), + abs((int)a.m - b.m)) < m_thresh; + } +}; + +//----------------------------------------------------------------------------------------- + +template <> +class PixelSelector +{ +public: + typedef TPixelCM32 pixel_type; + +private: + int m_thresh; + +public: + PixelSelector(int thresh) : m_thresh(thresh) {} + + bool areEqual(const TPixelCM32 &a, const TPixelCM32 &b) const + { + return (a.getInk() == b.getInk()) && (abs(a.getTone() - b.getTone()) < m_thresh); + } +}; + +//----------------------------------------------------------------------------------------- + +template +inline void weightPix(PIX *out, const PIX *a, const PIX *b, double weightA, double weightB) +{ + out->r = a->r * weightA + b->r * weightB; + out->g = a->g * weightA + b->g * weightB; + out->b = a->b * weightA + b->b * weightB; + out->m = a->m * weightA + b->m * weightB; +} + +//----------------------------------------------------------------------------------------- + +template <> +inline void weightPix(TPixelCM32 *out, const TPixelCM32 *a, const TPixelCM32 *b, + double weightA, double weightB) +{ + *out = TPixelCM32( + out->isPurePaint() ? b->getInk() : a->getInk(), a->getPaint(), + a->getTone() * weightA + b->getTone() * weightB); +} + +//----------------------------------------------------------------------------------------- + +//Returns 0 if pixels to connect are on the 00-11 diagonal, 1 on the 01-10 one. +template +inline bool checkNeighbourHood(int x, int y, PIX *pix, int lx, int ly, int dx, int dy, + const SELECTOR &sel) +{ + int count1 = 0, count2 = 0; + int dx2 = 2 * dx, dy2 = 2 * dy; + if (y > 1) { + //Lower edge + count1 += + (int)sel.areEqual(*(pix - dx), *(pix - dy2)) + + (int)sel.areEqual(*(pix - dx), *(pix - dy2 - dx)); + count2 += + (int)sel.areEqual(*pix, *(pix - dy2)) + + (int)sel.areEqual(*pix, *(pix - dy2 - dx)); + } + if (y < ly - 1) { + //Upper edge + count1 += + (int)sel.areEqual(*(pix - dx), *(pix + dy)) + + (int)sel.areEqual(*(pix - dx), *(pix + dy - dx)); + count2 += + (int)sel.areEqual(*pix, *(pix + dy)) + + (int)sel.areEqual(*pix, *(pix + dy - dx)); + } + if (x > 1) { + //Left edge + count1 += + (int)sel.areEqual(*(pix - dx), *(pix - dx2)) + + (int)sel.areEqual(*(pix - dx), *(pix - dx2 - dy)); + count2 += + (int)sel.areEqual(*pix, *(pix - dx2)) + + (int)sel.areEqual(*pix, *(pix - dx2 - dy)); + } + if (x < lx - 1) { + //Left edge + count1 += + (int)sel.areEqual(*(pix - dx), *(pix + dx)) + + (int)sel.areEqual(*(pix - dx), *(pix + dx - dy)); + count2 += + (int)sel.areEqual(*pix, *(pix + dx)) + + (int)sel.areEqual(*pix, *(pix + dx - dy)); + } + + //Connect by minority: if there are more pixels like those on the 00-11 diagonal, connect the other, + //and viceversa. + return count1 > count2; +} +} + +//======================================================================================== + +template +inline void filterLine(PIX *inLPix, PIX *inUPix, PIX *outLPix, PIX *outUPix, + int ll, int inDl, int outLDl, int outUDl, + double hStart, double slope, bool filterLower) +{ + assert(hStart >= 0.0 && slope > 0.0); + + double h0 = hStart, h1, area; + double base = hStart / slope; + + int i, end = tmin(tfloor(base), ll); + if (filterLower) { + //Filter lower line + for (i = 0; i < end; ++i, h0 = h1, + inLPix += inDl, inUPix += inDl, outLPix += outLDl) { + h1 = h0 - slope; + area = 0.5 * (h0 + h1); + weightPix(outLPix, outLPix, inUPix, 1.0 - area, area); + } + + if (i < ll) { + double remnant = base - end; + area = 0.5 * remnant * h0; + weightPix(outLPix, outLPix, inUPix, 1.0 - area, area); + } + } else { + //Filter upper line + for (i = 0; i < end; ++i, h0 = h1, + inLPix += inDl, inUPix += inDl, outUPix += outUDl) { + h1 = h0 - slope; + area = 0.5 * (h0 + h1); + weightPix(outUPix, outUPix, inLPix, 1.0 - area, area); + } + + if (i < ll) { + double remnant = base - end; + area = 0.5 * remnant * h0; + weightPix(outUPix, outUPix, inLPix, 1.0 - area, area); + } + } +} + +//--------------------------------------------------------------------------------------- + +template +inline bool checkLength(int lLine, int y, int ly, int dy, + PIX *pixL1, PIX *pixU1, PIX *pixL2, PIX *pixU2, + bool uniteU, bool do1Line, + const SELECTOR &sel) +{ + //1-length edges must be processed (as primary edges) only if explicitly required, + //and only when its associated secondary edge is of the same length. + + return (lLine > 1) || + (do1Line && + ((uniteU && (y > 1 && !(sel.areEqual(*pixL1, *(pixL1 - dy)) && sel.areEqual(*pixL2, *(pixL2 - dy))))) || + (y < ly - 1 && !(sel.areEqual(*pixU1, *(pixU1 + dy)) && sel.areEqual(*pixU2, *(pixU2 + dy)))))); +} + +//--------------------------------------------------------------------------------------- + +template +void processLine(int r, int lx, int ly, + PIX *inLRow, PIX *inURow, PIX *outLRow, PIX *outURow, + int inDx, int inDy, int outLDx, int outUDx, + bool do1Line, double hStart, double slope, + const SELECTOR &sel) +{ + //Using a 'horizontal' notation here - but the same applies in vertical too + ++r; + + //As long as we don't reach row end, process uninterrupted separation lines + //between colors + + PIX *inLL = inLRow, *inLR, *inUL = inURow, *inUR; + PIX *inLL_1, *inUL_1, *inLR_1, *inUR_1; + PIX *inLEnd = inLRow + lx * inDx; + int x, lLine; + bool uniteLL, uniteUL, uniteLR, uniteUR; + + //Special case: a line at row start has different weights + if (!sel.areEqual(*inLL, *inUL)) { + //Look for line ends + for (inLR = inLL + inDx, inUR = inUL + inDx; + inLR != inLEnd && sel.areEqual(*inLL, *inLR) && sel.areEqual(*inUL, *inUR); + inLR += inDx, inUR += inDx) + ; + + if (inLR != inLEnd) { + //Found a line to process + lLine = (inLR - inLL) / inDx; + + inLR_1 = inLR - inDx, inUR_1 = inUR - inDx; + x = (inLR_1 - inLRow) / inDx; + + uniteUR = sel.areEqual(*inUR_1, *inLR); + uniteLR = sel.areEqual(*inLR_1, *inUR); + if (uniteUR || uniteLR) { + if (uniteUR && uniteLR) + //Ambiguous case. Check neighborhood to find out which one must be actually united. + uniteUR = !checkNeighbourHood(x + 1, r, inUR, lx, ly, inDx, inDy, sel); + + if (checkLength(lLine, r, ly, inDy, inLR_1, inUR_1, inLR, inUR, uniteUR, do1Line, sel)) + filterLine(inLR_1, inUR_1, outLRow + x * outLDx, outURow + x * outUDx, + lLine, -inDx, -outLDx, -outUDx, hStart, slope / (lLine << 1), uniteUR); + } + } + + //Update lefts + inLL = inLR, inUL = inUR; + } + + //Search for a line start + for (; inLL != inLEnd && sel.areEqual(*inLL, *inUL); inLL += inDx, inUL += inDx) + ; + + while (inLL != inLEnd) { + //Look for line ends + for (inLR = inLL + inDx, inUR = inUL + inDx; + inLR != inLEnd && sel.areEqual(*inLL, *inLR) && sel.areEqual(*inUL, *inUR); + inLR += inDx, inUR += inDx) + ; + + if (inLR == inLEnd) + break; //Dealt with later + + //Found a line to process + lLine = (inLR - inLL) / inDx; + + //First, filter left to right + inLL_1 = inLL - inDx, inUL_1 = inUL - inDx; + x = (inLL - inLRow) / inDx; + + uniteUL = sel.areEqual(*inUL, *inLL_1); + uniteLL = sel.areEqual(*inLL, *inUL_1); + if (uniteUL || uniteLL) { + if (uniteUL && uniteLL) + uniteUL = checkNeighbourHood(x, r, inUL, lx, ly, inDx, inDy, sel); + + if (checkLength(lLine, r, ly, inDy, inLL_1, inUL_1, inLL, inUL, uniteUL, do1Line, sel)) + filterLine(inLL, inUL, outLRow + x * outLDx, outURow + x * outUDx, + lLine, inDx, outLDx, outUDx, hStart, slope / lLine, uniteUL); + } + + //Then, filter right to left + inLR_1 = inLR - inDx, inUR_1 = inUR - inDx; + x = (inLR_1 - inLRow) / inDx; + + uniteUR = sel.areEqual(*inUR_1, *inLR); + uniteLR = sel.areEqual(*inLR_1, *inUR); + if (uniteUR || uniteLR) { + if (uniteUR && uniteLR) + uniteUR = !checkNeighbourHood(x + 1, r, inUR, lx, ly, inDx, inDy, sel); + + if (checkLength(lLine, r, ly, inDy, inLR_1, inUR_1, inLR, inUR, uniteUR, do1Line, sel)) + filterLine(inLR_1, inUR_1, outLRow + x * outLDx, outURow + x * outUDx, + lLine, -inDx, -outLDx, -outUDx, hStart, slope / lLine, uniteUR); + } + + //Update lefts - search for a new line start + inLL = inLR, inUL = inUR; + for (; inLL != inLEnd && sel.areEqual(*inLL, *inUL); inLL += inDx, inUL += inDx) + ; + } + + //Special case: filter the last line in the row + if (inLL != inLEnd) { + //Found a line to process + lLine = (inLR - inLL) / inDx; + + inLL_1 = inLL - inDx, inUL_1 = inUL - inDx; + x = (inLL - inLRow) / inDx; + + uniteUL = sel.areEqual(*inUL, *inLL_1); + uniteLL = sel.areEqual(*inLL, *inUL_1); + if (uniteUL || uniteLL) { + if (uniteUL && uniteLL) + uniteUL = checkNeighbourHood(x, r, inUL, lx, ly, inDx, inDy, sel); + + if (checkLength(lLine, r, ly, inDy, inLL_1, inUL_1, inLL, inUL, uniteUL, do1Line, sel)) + filterLine(inLL, inUL, outLRow + x * outLDx, outURow + x * outUDx, + lLine, inDx, outLDx, outUDx, hStart, slope / (lLine << 1), uniteUL); + } + } +} + +//--------------------------------------------------------------------------------------- + +template +void makeAntialias(const TRasterPT &src, TRasterPT &dst, int threshold, int softness) +{ + dst->copy(src); + if (softness == 0) + return; + + double slope = (50.0 / softness); + double hStart = 0.5; //fixed for now + + src->lock(); + dst->lock(); + + PixelSelector sel(threshold); + + //First, filter by rows + int x, y, lx = src->getLx(), ly = src->getLy(), lx_1 = lx - 1, ly_1 = ly - 1; + for (y = 0; y < ly_1; ++y) { + processLine(y, lx, ly, + src->pixels(y), src->pixels(y + 1), + dst->pixels(y), dst->pixels(y + 1), + 1, src->getWrap(), 1, 1, + true, hStart, slope, + sel); + } + + //Then, go by columns + for (x = 0; x < lx_1; ++x) { + processLine(x, ly, lx, + src->pixels(0) + x, src->pixels(0) + x + 1, + dst->pixels(0) + x, dst->pixels(0) + x + 1, + src->getWrap(), 1, dst->getWrap(), dst->getWrap(), + false, hStart, slope, + sel); + } + + dst->unlock(); + src->unlock(); +} + +//--------------------------------------------------------------------------------------- + +void TRop::antialias(const TRasterP &src, const TRasterP &dst, int threshold, int softness) +{ + assert(src->getSize() == dst->getSize()); + + TRaster32P src32(src), dst32(dst); + if (src32 && dst32) { + makeAntialias(src32, dst32, threshold, softness); + return; + } + + TRaster64P src64(src), dst64(dst); + if (src64 && dst64) { + makeAntialias(src64, dst64, threshold << 8, softness); + return; + } + + TRasterCM32P srcCM(src), dstCM(dst); + if (srcCM && dstCM) { + makeAntialias(srcCM, dstCM, threshold, softness); + return; + } + + assert(!"Source and destination rasters must be of the same type!"); +} diff --git a/toonz/sources/common/trop/tautoclose.cpp b/toonz/sources/common/trop/tautoclose.cpp new file mode 100644 index 0000000..eefd152 --- /dev/null +++ b/toonz/sources/common/trop/tautoclose.cpp @@ -0,0 +1,1190 @@ + + +#include "texception.h" +#include "tautoclose.h" +#include "trastercm.h" +#include "skeletonlut.h" + +#define AUT_SPOT_SAMPLES 10 + +using namespace SkeletonLut; + +class TAutocloser::Imp +{ +public: + struct Seed { + UCHAR *m_ptr; + UCHAR m_preseed; + Seed(UCHAR *ptr, UCHAR preseed) : m_ptr(ptr), m_preseed(preseed) {} + }; + + int m_closingDistance; + double m_spotAngle; + int m_inkIndex; + TRasterP m_raster; + TRasterGR8P m_bRaster; + UCHAR *m_br; + + int m_bWrap; + + int m_displaceVector[8]; + TPointD m_displAverage; + int m_visited; + + double m_csp, m_snp, m_csm, m_snm, m_csa, m_sna, m_csb, m_snb; + + Imp(const TRasterP &r) + : m_raster(r), m_spotAngle((180 * TConsts::pi) / 360.0), m_closingDistance(10), m_inkIndex(0) + { + } + + ~Imp() {} + + bool inline isInk(UCHAR *br) { return (*br) & 0x1; } + inline void eraseInk(UCHAR *br) { *(br) &= 0xfe; } + + UCHAR inline ePix(UCHAR *br) { return (*(br + 1)); } + UCHAR inline wPix(UCHAR *br) { return (*(br - 1)); } + UCHAR inline nPix(UCHAR *br) { return (*(br + m_bWrap)); } + UCHAR inline sPix(UCHAR *br) { return (*(br - m_bWrap)); } + UCHAR inline swPix(UCHAR *br) { return (*(br - m_bWrap - 1)); } + UCHAR inline nwPix(UCHAR *br) { return (*(br + m_bWrap - 1)); } + UCHAR inline nePix(UCHAR *br) { return (*(br + m_bWrap + 1)); } + UCHAR inline sePix(UCHAR *br) { return (*(br - m_bWrap + 1)); } + UCHAR inline neighboursCode(UCHAR *seed) + { + return ((swPix(seed) & 0x1) | ((sPix(seed) & 0x1) << 1) | + ((sePix(seed) & 0x1) << 2) | ((wPix(seed) & 0x1) << 3) | + ((ePix(seed) & 0x1) << 4) | ((nwPix(seed) & 0x1) << 5) | + ((nPix(seed) & 0x1) << 6) | ((nePix(seed) & 0x1) << 7)); + } + + //....................... + + inline bool notMarkedBorderInk(UCHAR *br) + { + return ((((*br) & 0x5) == 1) && (ePix(br) == 0 || wPix(br) == 0 || nPix(br) == 0 || sPix(br) == 0)); + } + + //....................... + UCHAR *getPtr(int x, int y) { return m_br + m_bWrap * y + x; } + UCHAR *getPtr(const TPoint &p) { return m_br + m_bWrap * p.y + p.x; } + + TPoint getCoordinates(UCHAR *br) + { + TPoint p; + int pixelCount = br - m_bRaster->getRawData(); + p.y = pixelCount / m_bWrap; + p.x = pixelCount - p.y * m_bWrap; + return p; + } + + //....................... + void compute(vector &closingSegmentArray); + void skeletonize(vector &endpoints); + void findSeeds(vector &seeds, vector &endpoints); + void erase(vector &seeds, vector &endpoints); + void circuitAndMark(UCHAR *seed, UCHAR preseed); + bool circuitAndCancel(UCHAR *seed, UCHAR preseed, vector &endpoints); + void findMeetingPoints(vector &endpoints, vector &closingSegments); + void calculateWeightAndDirection(vector &orientedEndpoints); + bool spotResearchTwoPoints(vector &endpoints, vector &closingSegments); + bool spotResearchOnePoint(vector &endpoints, vector &closingSegments); + + void copy(const TRasterGR8P &braux, TRaster32P &raux); + int exploreTwoSpots(const TAutocloser::Segment &s0, const TAutocloser::Segment &s1); + int notInsidePath(const TPoint &p, const TPoint &q); + void drawInByteRaster(const TPoint &p0, const TPoint &p1); + TPoint visitEndpoint(UCHAR *br); + bool exploreSpot(const Segment &s, TPoint &p); + bool exploreRay(UCHAR *br, Segment s, TPoint &p); + void visitPix(UCHAR *br, int toVisit, const TPoint &dis); + void cancelMarks(UCHAR *br); + void cancelFromArray(vector &array, TPoint p, int &count); +}; + +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ + +#define DRAW_SEGMENT(a, b, da, db, istr1, istr2, block) \ + { \ + d = 2 * db - da; \ + incr_1 = 2 * db; \ + incr_2 = 2 * (db - da); \ + while (a < da) { \ + if (d <= 0) { \ + d += incr_1; \ + a++; \ + istr1; \ + } else { \ + d += incr_2; \ + a++; \ + b++; \ + istr2; \ + } \ + block; \ + } \ + } + +/*------------------------------------------------------------------------*/ + +#define EXPLORE_RAY_ISTR(istr) \ + if (!inside_ink) { \ + if (((*br) & 0x1) && !((*br) & 0x80)) { \ + p.x = istr; \ + p.y = (s.first.y < s.second.y) ? s.first.y + y : s.first.y - y; \ + return true; \ + } \ + } else if (inside_ink && !((*br) & 0x1)) \ + inside_ink = 0; + +/*------------------------------------------------------------------------*/ + +//------------------------------------------------- + +namespace +{ + +inline bool isInk(const TPixel32 &pix) +{ + return pix.r < 80; +} + +/*------------------------------------------------------------------------*/ + +TRasterGR8P makeByteRaster(const TRasterCM32P &r) +{ + int lx = r->getLx(); + int ly = r->getLy(); + TRasterGR8P bRaster(lx + 4, ly + 4); + int i, j; + + //bRaster->create(lx+4, ly+4); + bRaster->lock(); + UCHAR *br = bRaster->getRawData(); + + for (i = 0; i < lx + 4; i++) + *(br++) = 0; + + for (i = 0; i < lx + 4; i++) + *(br++) = 131; + + r->lock(); + for (i = 0; i < ly; i++) { + *(br++) = 0; + *(br++) = 131; + TPixelCM32 *pix = r->pixels(i); + for (j = 0; j < lx; j++, pix++) { + if (pix->getTone() != pix->getMaxTone()) + *(br++) = 3; + else + *(br++) = 0; + } + *(br++) = 131; + *(br++) = 0; + } + r->unlock(); + for (i = 0; i < lx + 4; i++) + *(br++) = 131; + + for (i = 0; i < lx + 4; i++) + *(br++) = 0; + + bRaster->unlock(); + return bRaster; +} + +/*------------------------------------------------------------------------*/ + +#define SET_INK \ + if (buf->getTone() == buf->getMaxTone()) \ + *buf = TPixelCM32(inkIndex, 22, 0); + +void drawSegment(TRasterCM32P &r, const TAutocloser::Segment &s, USHORT inkIndex) +{ + int wrap = r->getWrap(); + TPixelCM32 *buf = r->pixels(); + /* +int i, j; +for (i=0; igetLy();i++) + { + for (j=0; jgetLx();j++, buf++) + *buf = (1<<4)|0xf; + buf += wrap-r->getLx(); + } +return; +*/ + + int x, y, dx, dy, d, incr_1, incr_2; + + int x1 = s.first.x; + int y1 = s.first.y; + int x2 = s.second.x; + int y2 = s.second.y; + + if (x1 > x2) { + tswap(x1, x2); + tswap(y1, y2); + } + + buf += y1 * wrap + x1; + + dx = x2 - x1; + dy = y2 - y1; + + x = y = 0; + + if (dy >= 0) { + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (buf++), (buf += wrap + 1), SET_INK) + else + DRAW_SEGMENT(y, x, dy, dx, (buf += wrap), (buf += wrap + 1), SET_INK) + } else { + dy = -dy; + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (buf++), (buf -= (wrap - 1)), SET_INK) + else + DRAW_SEGMENT(y, x, dy, dx, (buf -= wrap), (buf -= (wrap - 1)), SET_INK) + } +} + +/*------------------------------------------------------------------------*/ + +} //namespace +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::compute(vector &closingSegmentArray) +{ + vector endpoints; + try { + + assert(closingSegmentArray.empty()); + + TRasterCM32P raux; + + if (!(raux = (TRasterCM32P)m_raster)) + throw TException("Unable to autoclose a not CM32 image."); + + if (m_raster->getLx() == 0 || m_raster->getLy() == 0) + throw TException("Autoclose error: bad image size"); + + //Lx = r->lx; + //Ly = r->ly; + + TRasterGR8P braux = makeByteRaster(raux); + + m_bRaster = braux->extract(TRect(2, 2, braux->getLx() - 3, braux->getLy() - 3)); + m_bRaster->lock(); + m_br = m_bRaster->getRawData(); + m_bWrap = m_bRaster->getWrap(); + + m_displaceVector[0] = -m_bWrap - 1; + m_displaceVector[1] = -m_bWrap; + m_displaceVector[2] = -m_bWrap + 1; + m_displaceVector[3] = -1; + m_displaceVector[4] = +1; + m_displaceVector[5] = m_bWrap - 1; + m_displaceVector[6] = m_bWrap; + m_displaceVector[7] = m_bWrap + 1; + + skeletonize(endpoints); + + findMeetingPoints(endpoints, closingSegmentArray); + raux->lock(); + for (int i = 0; i < (int)closingSegmentArray.size(); i++) + drawSegment(raux, closingSegmentArray[i], m_inkIndex); + raux->unlock(); + //copy(m_bRaster, raux); + m_bRaster->unlock(); + m_br = 0; + } + + catch (TException &e) { + throw e; + } +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::copy(const TRasterGR8P &br, TRaster32P &r) +{ + assert(r->getLx() == br->getLx() && r->getLy() == br->getLy()); + int i, j; + + int lx = r->getLx(); + int ly = r->getLy(); + br->lock(); + r->lock(); + UCHAR *bbuf = br->getRawData(); + TPixel *buf = (TPixel *)r->getRawData(); + + for (i = 0; i < ly; i++) { + for (j = 0; j < lx; j++, buf++, bbuf++) { + buf->m = 255; + if ((*bbuf) & 0x40) + buf->r = 255, buf->g = buf->b = 0; + else if (isInk(bbuf)) + buf->r = buf->g = buf->b = 0; + else + buf->r = buf->g = buf->b = 255; + } + buf += r->getWrap() - lx; + bbuf += br->getWrap() - lx; + } + + br->unlock(); + r->unlock(); +} + +/*=============================================================================*/ + +namespace +{ + +int intersect_segment(int x1, int y1, int x2, int y2, int i, double *ris) +{ + if ((i < tmin(y1, y2)) || (i > tmax(y1, y2)) || (y1 == y2)) + return 0; + + *ris = ((double)((x1 - x2) * (i - y2)) / (double)(y1 - y2) + x2); + + return 1; +} + +/*=============================================================================*/ + +inline int distance2(const TPoint p0, const TPoint p1) +{ + return (p0.x - p1.x) * (p0.x - p1.x) + (p0.y - p1.y) * (p0.y - p1.y); +} + +/*=============================================================================*/ + +int closerPoint(const vector &points, vector &marks, int index) +{ + assert(points.size() == marks.size()); + + int min, curr; + int minval = 9999999; + + min = index + 1; + + for (curr = index + 1; curr < (int)points.size(); curr++) + if (!(marks[curr])) { + int distance = distance2(points[index].first, points[curr].first); + + if (distance < minval) { + minval = distance; + min = curr; + } + } + + marks[min] = true; + return min; +} + +/*------------------------------------------------------------------------*/ + +int intersect_triangle(int x1a, int y1a, int x2a, int y2a, int x3a, int y3a, + int x1b, int y1b, int x2b, int y2b, int x3b, int y3b) +{ + int minx, maxx, miny, maxy, i; + double xamin, xamax, xbmin, xbmax, val; + + miny = tmax(tmin(y1a, y2a, y3a), tmin(y1b, y2b, y3b)); + maxy = tmin(tmax(y1a, y2a, y3a), tmax(y1b, y2b, y3b)); + if (maxy < miny) + return 0; + + minx = tmax(tmin(x1a, x2a, x3a), tmin(x1b, x2b, x3b)); + maxx = tmin(tmax(x1a, x2a, x3a), tmax(x1b, x2b, x3b)); + if (maxx < minx) + return 0; + + for (i = miny; i <= maxy; i++) { + xamin = xamax = xbmin = xbmax = 0.0; + + intersect_segment(x1a, y1a, x2a, y2a, i, &xamin); + + if (intersect_segment(x1a, y1a, x3a, y3a, i, &val)) + if (xamin) + xamax = val; + else + xamin = val; + + if (!xamax) + intersect_segment(x2a, y2a, x3a, y3a, i, &xamax); + + if (xamax < xamin) { + val = xamin, xamin = xamax, xamax = val; + } + + intersect_segment(x1b, y1b, x2b, y2b, i, &xbmin); + + if (intersect_segment(x1b, y1b, x3b, y3b, i, &val)) + if (xbmin) + xbmax = val; + else + xbmin = val; + + if (!xbmax) + intersect_segment(x2b, y2b, x3b, y3b, i, &xbmax); + + if (xbmax < xbmin) { + val = xbmin, xbmin = xbmax, xbmax = val; + } + + if (!((tceil(xamax) < tfloor(xbmin)) || (tceil(xbmax) < tfloor(xamin)))) + return 1; + } + return 0; +} + +/*------------------------------------------------------------------------*/ + +} //namespace + +/*------------------------------------------------------------------------*/ + +int TAutocloser::Imp::notInsidePath(const TPoint &p, const TPoint &q) +{ + int tmp, x, y, dx, dy, d, incr_1, incr_2; + int x1, y1, x2, y2; + + x1 = p.x; + y1 = p.y; + x2 = q.x; + y2 = q.y; + + if (x1 > x2) { + tmp = x1, x1 = x2, x2 = tmp; + tmp = y1, y1 = y2, y2 = tmp; + } + UCHAR *br = getPtr(x1, y1); + + dx = x2 - x1; + dy = y2 - y1; + x = y = 0; + + if (dy >= 0) { + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br += m_bWrap + 1), if (!((*br) & 0x2)) return true) + else + DRAW_SEGMENT(y, x, dy, dx, (br += m_bWrap), (br += m_bWrap + 1), if (!((*br) & 0x2)) return true) + } else { + dy = -dy; + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br -= m_bWrap - 1), if (!((*br) & 0x2)) return true) + else + DRAW_SEGMENT(y, x, dy, dx, (br -= m_bWrap), (br -= m_bWrap - 1), if (!((*br) & 0x2)) return true) + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +int TAutocloser::Imp::exploreTwoSpots(const TAutocloser::Segment &s0, const TAutocloser::Segment &s1) +{ + int x1a, y1a, x2a, y2a, x3a, y3a, x1b, y1b, x2b, y2b, x3b, y3b; + + x1a = s0.first.x; + y1a = s0.first.y; + x1b = s1.first.x; + y1b = s1.first.y; + + TPoint p0aux = s0.second; + TPoint p1aux = s1.second; + + if (x1a == p0aux.x && y1a == p0aux.y) + return 0; + if (x1b == p1aux.x && y1b == p1aux.y) + return 0; + + x2a = tround(x1a + (p0aux.x - x1a) * m_csp - (p0aux.y - y1a) * m_snp); + y2a = tround(y1a + (p0aux.x - x1a) * m_snp + (p0aux.y - y1a) * m_csp); + x3a = tround(x1a + (p0aux.x - x1a) * m_csm - (p0aux.y - y1a) * m_snm); + y3a = tround(y1a + (p0aux.x - x1a) * m_snm + (p0aux.y - y1a) * m_csm); + + x2b = tround(x1b + (p1aux.x - x1b) * m_csp - (p1aux.y - y1b) * m_snp); + y2b = tround(y1b + (p1aux.x - x1b) * m_snp + (p1aux.y - y1b) * m_csp); + x3b = tround(x1b + (p1aux.x - x1b) * m_csm - (p1aux.y - y1b) * m_snm); + y3b = tround(y1b + (p1aux.x - x1b) * m_snm + (p1aux.y - y1b) * m_csm); + + return (intersect_triangle(x1a, y1a, p0aux.x, p0aux.y, x2a, y2a, + x1b, y1b, p1aux.x, p1aux.y, x2b, y2b) || + intersect_triangle(x1a, y1a, p0aux.x, p0aux.y, x3a, y3a, + x1b, y1b, p1aux.x, p1aux.y, x2b, y2b) || + intersect_triangle(x1a, y1a, p0aux.x, p0aux.y, x2a, y2a, + x1b, y1b, p1aux.x, p1aux.y, x3b, y3b) || + intersect_triangle(x1a, y1a, p0aux.x, p0aux.y, x3a, y3a, + x1b, y1b, p1aux.x, p1aux.y, x3b, y3b)); +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::findMeetingPoints(vector &endpoints, + vector &closingSegments) +{ + int i; + double alfa; + alfa = m_spotAngle / AUT_SPOT_SAMPLES; + m_csp = cos(m_spotAngle / 5); + m_snp = sin(m_spotAngle / 5); + m_csm = cos(-m_spotAngle / 5); + m_snm = sin(-m_spotAngle / 5); + m_csa = cos(alfa); + m_sna = sin(alfa); + m_csb = cos(-alfa); + m_snb = sin(-alfa); + + vector orientedEndpoints(endpoints.size()); + for (i = 0; i < (int)endpoints.size(); i++) + orientedEndpoints[i].first = endpoints[i]; + + int size = -1; + + while ((int)closingSegments.size() > size && !orientedEndpoints.empty()) { + size = closingSegments.size(); + do + calculateWeightAndDirection(orientedEndpoints); + while (spotResearchTwoPoints(orientedEndpoints, closingSegments)); + + do + calculateWeightAndDirection(orientedEndpoints); + while (spotResearchOnePoint(orientedEndpoints, closingSegments)); + } +} + +/*------------------------------------------------------------------------*/ + +bool allMarked(const vector &marks, int index) +{ + int i; + + for (i = index + 1; i < (int)marks.size(); i++) + if (!marks[i]) + return false; + return true; +} + +/*------------------------------------------------------------------------*/ + +bool TAutocloser::Imp::spotResearchTwoPoints(vector &endpoints, vector &closingSegments) +{ + int i, distance, current = 0, closerIndex; + int sqrDistance = m_closingDistance * m_closingDistance; + bool found = 0; + vector marks(endpoints.size()); + + while (current < (int)endpoints.size() - 1) { + found = 0; + for (i = current + 1; i < (int)marks.size(); i++) + marks[i] = false; + distance = 0; + + while (!found && (distance <= sqrDistance) && !allMarked(marks, current)) { + closerIndex = closerPoint(endpoints, marks, current); + if (exploreTwoSpots(endpoints[current], endpoints[closerIndex]) && + notInsidePath(endpoints[current].first, endpoints[closerIndex].first)) { + drawInByteRaster(endpoints[current].first, endpoints[closerIndex].first); + closingSegments.push_back(Segment(endpoints[current].first, endpoints[closerIndex].first)); + + if (!EndpointTable[neighboursCode(getPtr(endpoints[closerIndex].first))]) { + std::vector::iterator it = endpoints.begin(); + std::advance(it, closerIndex); + endpoints.erase(it); + std::vector::iterator it1 = marks.begin(); + std::advance(it1, closerIndex); + marks.erase(it1); + } + found = true; + } + } + + if (found) { + std::vector::iterator it = endpoints.begin(); + std::advance(it, current); + endpoints.erase(it); + std::vector::iterator it1 = marks.begin(); + std::advance(it1, current); + marks.erase(it1); + } else + current++; + } + return found; +} + +/*------------------------------------------------------------------------*/ +/* +static void clear_marks(POINT *p) +{ +while (p) + { + p->mark = 0; + p = p->next; + } +} + + +static int there_are_unmarked(POINT *p) +{ +while (p) + { + if (!p->mark) return 1; + p = p->next; + } +return 0; +} +*/ + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::calculateWeightAndDirection(vector &orientedEndpoints) +{ + //UCHAR *br; + int lx = m_raster->getLx(); + int ly = m_raster->getLy(); + + std::vector::iterator it = orientedEndpoints.begin(); + + while (it != orientedEndpoints.end()) { + TPoint p0 = it->first; + TPoint &p1 = it->second; + + //br = (UCHAR *)m_bRaster->pixels(p0.y)+p0.x; + //code = neighboursCode(br); + /*if (!EndpointTable[code]) + { + it = orientedEndpoints.erase(it); + continue; + }*/ + TPoint displAverage = visitEndpoint(getPtr(p0)); + + p1 = p0 - displAverage; + + /*if ((point->x2<0 && point->y2<0) || (point->x2>Lx && point->y2>Ly)) printf("che palle!!!!!!\n");*/ + + if (p1.x < 0) { + p1.y = tround(p0.y - (float)((p0.y - p1.y) * p0.x) / (p0.x - p1.x)); + p1.x = 0; + } else if (p1.x > lx) { + p1.y = tround(p0.y - (float)((p0.y - p1.y) * (p0.x - lx)) / (p0.x - p1.x)); + p1.x = lx; + } + + if (p1.y < 0) { + p1.x = tround(p0.x - (float)((p0.x - p1.x) * p0.y) / (p0.y - p1.y)); + p1.y = 0; + } else if (p1.y > ly) { + p1.x = tround(p0.x - (float)((p0.x - p1.x) * (p0.y - ly)) / (p0.y - p1.y)); + p1.y = ly; + } + it++; + } +} + +/*------------------------------------------------------------------------*/ + +bool TAutocloser::Imp::spotResearchOnePoint(vector &endpoints, vector &closingSegments) +{ + int count = 0; + bool ret = false; + + while (count < (int)endpoints.size()) { + TPoint p; + + if (exploreSpot(endpoints[count], p)) { + ret = true; + drawInByteRaster(endpoints[count].first, p); + closingSegments.push_back(Segment(endpoints[count].first, p)); + cancelFromArray(endpoints, p, count); + if (!EndpointTable[neighboursCode(getPtr(endpoints[count].first))]) { + std::vector::iterator it = endpoints.begin(); + std::advance(it, count); + endpoints.erase(it); + continue; + } + } + count++; + } + + return ret; +} + +/*------------------------------------------------------------------------*/ + +bool TAutocloser::Imp::exploreSpot(const Segment &s, TPoint &p) +{ + int x1, y1, x2, y2, x3, y3, i; + double x2a, y2a, x2b, y2b, xnewa, ynewa, xnewb, ynewb; + int lx = m_raster->getLx(); + int ly = m_raster->getLy(); + + x1 = s.first.x; + y1 = s.first.y; + x2 = s.second.x; + y2 = s.second.y; + + if (x1 == x2 && y1 == y2) + return 0; + + if (exploreRay(getPtr(x1, y1), s, p)) + return true; + + x2a = x2b = (double)x2; + y2a = y2b = (double)y2; + + for (i = 0; i < AUT_SPOT_SAMPLES; i++) { + xnewa = x1 + (x2a - x1) * m_csa - (y2a - y1) * m_sna; + ynewa = y1 + (y2a - y1) * m_csa + (x2a - x1) * m_sna; + x3 = tround(xnewa); + y3 = tround(ynewa); + if ((x3 != tround(x2a) || y3 != tround(y2a)) && x3 > 0 && x3 < lx && y3 > 0 && y3 < ly && + exploreRay(getPtr(x1, y1), Segment(TPoint(x1, y1), TPoint(tround(xnewa), tround(ynewa))), p)) + return true; + + x2a = xnewa; + y2a = ynewa; + + xnewb = x1 + (x2b - x1) * m_csb - (y2b - y1) * m_snb; + ynewb = y1 + (y2b - y1) * m_csb + (x2b - x1) * m_snb; + x3 = tround(xnewb); + y3 = tround(ynewb); + if ((x3 != tround(x2b) || y3 != tround(y2b)) && x3 > 0 && x3 < lx && y3 > 0 && y3 < ly && + exploreRay(getPtr(x1, y1), Segment(TPoint(x1, y1), TPoint(tround(xnewb), tround(ynewb))), p)) + return true; + + x2b = xnewb; + y2b = ynewb; + } + return false; +} + +/*------------------------------------------------------------------------*/ + +bool TAutocloser::Imp::exploreRay(UCHAR *br, Segment s, TPoint &p) +{ + int x, y, dx, dy, d, incr_1, incr_2, inside_ink; + + inside_ink = 1; + + x = 0; + y = 0; + + if (s.first.x < s.second.x) { + dx = s.second.x - s.first.x; + dy = s.second.y - s.first.y; + if (dy >= 0) + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br += m_bWrap + 1), + EXPLORE_RAY_ISTR((s.first.x + x))) + else + DRAW_SEGMENT(y, x, dy, dx, (br += m_bWrap), (br += m_bWrap + 1), + EXPLORE_RAY_ISTR((s.first.x + x))) + else { + dy = -dy; + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br -= m_bWrap - 1), + EXPLORE_RAY_ISTR((s.first.x + x))) + else + DRAW_SEGMENT(y, x, dy, dx, (br -= m_bWrap), (br -= m_bWrap - 1), + EXPLORE_RAY_ISTR((s.first.x + x))) + } + } else { + dx = s.first.x - s.second.x; + dy = s.second.y - s.first.y; + if (dy >= 0) + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br--), (br += m_bWrap - 1), + EXPLORE_RAY_ISTR((s.first.x - x))) + else + DRAW_SEGMENT(y, x, dy, dx, (br += m_bWrap), (br += m_bWrap - 1), + EXPLORE_RAY_ISTR((s.first.x - x))) + else { + dy = -dy; + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br--), (br -= m_bWrap + 1), + EXPLORE_RAY_ISTR((s.first.x - x))) + else + DRAW_SEGMENT(y, x, dy, dx, (br -= m_bWrap), (br -= m_bWrap + 1), + EXPLORE_RAY_ISTR((s.first.x - x))) + } + } + return false; +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::drawInByteRaster(const TPoint &p0, const TPoint &p1) +{ + int x, y, dx, dy, d, incr_1, incr_2; + UCHAR *br; + + if (p0.x > p1.x) { + br = getPtr(p1); + dx = p0.x - p1.x; + dy = p0.y - p1.y; + } else { + br = getPtr(p0); + dx = p1.x - p0.x; + dy = p1.y - p0.y; + } + + x = y = 0; + + if (dy >= 0) { + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br += m_bWrap + 1), ((*br) |= 0x41)) + else + DRAW_SEGMENT(y, x, dy, dx, (br += m_bWrap), (br += m_bWrap + 1), ((*br) |= 0x41)) + } else { + dy = -dy; + if (dy <= dx) + DRAW_SEGMENT(x, y, dx, dy, (br++), (br -= m_bWrap - 1), ((*br) |= 0x41)) + else + DRAW_SEGMENT(y, x, dy, dx, (br -= m_bWrap), (br -= m_bWrap - 1), ((*br) |= 0x41)) + } +} + +/*------------------------------------------------------------------------*/ + +TPoint TAutocloser::Imp::visitEndpoint(UCHAR *br) + +{ + m_displAverage = TPointD(); + + m_visited = 0; + + visitPix(br, m_closingDistance, TPoint()); + cancelMarks(br); + + return TPoint(convert((1.0 / m_visited) * m_displAverage)); +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::visitPix(UCHAR *br, int toVisit, const TPoint &dis) +{ + UCHAR b = 0; + int i, pixToVisit = 0; + + *br |= 0x10; + m_visited++; + m_displAverage.x += dis.x; + m_displAverage.y += dis.y; + + toVisit--; + if (toVisit == 0) + return; + + for (i = 0; i < 8; i++) { + UCHAR *v = br + m_displaceVector[i]; + if (isInk(v) && !((*v) & 0x10)) { + b |= (1 << i); + pixToVisit++; + } + } + + if (pixToVisit == 0) + return; + + if (pixToVisit <= 4) + toVisit = troundp(toVisit / (double)pixToVisit); + + if (toVisit == 0) + return; + + int x[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int y[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + for (i = 0; i < 8; i++) + if (b & (1 << i)) + visitPix(br + m_displaceVector[i], toVisit, dis + TPoint(x[i], y[i])); +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::cancelMarks(UCHAR *br) +{ + *br &= 0xef; + int i; + + for (i = 0; i < 8; i++) { + UCHAR *v = br + m_displaceVector[i]; + + if (isInk(v) && (*v) & 0x10) + cancelMarks(v); + } +} + +/*=============================================================================*/ + +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ +/*=============================================================================*/ + +/*=============================================================================*/ + +void TAutocloser::Imp::skeletonize(vector &endpoints) +{ + vector seeds; + + findSeeds(seeds, endpoints); + + erase(seeds, endpoints); +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::findSeeds(vector &seeds, vector &endpoints) +{ + int i, j; + UCHAR preseed; + + UCHAR *br = m_br; + + for (i = 0; i < m_bRaster->getLy(); i++) { + for (j = 0; j < m_bRaster->getLx(); j++, br++) { + if (notMarkedBorderInk(br)) { + preseed = FirstPreseedTable[neighboursCode(br)]; + + if (preseed != 8) /*non e' un pixel isolato*/ + { + seeds.push_back(Seed(br, preseed)); + circuitAndMark(br, preseed); + } else { + (*br) |= 0x8; + endpoints.push_back(getCoordinates(br)); + } + } + } + br += m_bWrap - m_bRaster->getLx(); + } +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::circuitAndMark(UCHAR *seed, UCHAR preseed) +{ + UCHAR *walker; + UCHAR displ, prewalker; + + *seed |= 0x4; + + displ = NextPointTable[(neighboursCode(seed) << 3) | preseed]; + assert(displ >= 0 && displ < 8); + + walker = seed + m_displaceVector[displ]; + prewalker = displ ^ 0x7; + + while ((walker != seed) || (preseed != prewalker)) { + *walker |= 0x4; /* metto la marca di passaggio */ + + displ = NextPointTable[(neighboursCode(walker) << 3) | prewalker]; + assert(displ >= 0 && displ < 8); + walker += m_displaceVector[displ]; + prewalker = displ ^ 0x7; + } + + return; +} + +/*------------------------------------------------------------------------*/ + +void TAutocloser::Imp::erase(vector &seeds, vector &endpoints) +{ + int i, size = 0, oldSize; + UCHAR *seed, preseed, code, displ; + oldSize = seeds.size(); + + while (oldSize != size) { + oldSize = size; + size = seeds.size(); + + for (i = oldSize; i < size; i++) { + seed = seeds[i].m_ptr; + preseed = seeds[i].m_preseed; + + if (!isInk(seed)) { + code = NextSeedTable[neighboursCode(seed)]; + seed += m_displaceVector[code & 0x7]; + preseed = (code & 0x38) >> 3; + } + + if (circuitAndCancel(seed, preseed, endpoints)) { + if (isInk(seed)) { + + displ = NextPointTable[(neighboursCode(seed) << 3) | preseed]; + assert(displ >= 0 && displ < 8); + seeds.push_back(Seed(seed + m_displaceVector[displ], displ ^ 0x7)); + + } else /* il seed e' stato cancellato */ + { + code = NextSeedTable[neighboursCode(seed)]; + seeds.push_back(Seed(seed + m_displaceVector[code & 0x7], (code & 0x38) >> 3)); + } + } + } + } +} + +/*------------------------------------------------------------------------*/ + +bool TAutocloser::Imp::circuitAndCancel(UCHAR *seed, UCHAR preseed, vector &endpoints) +{ + UCHAR *walker, *previous; + UCHAR displ, prewalker; + bool ret = false; + + displ = NextPointTable[(neighboursCode(seed) << 3) | preseed]; + assert(displ >= 0 && displ < 8); + + if ((displ == preseed) && !((*seed) & 0x8)) { + endpoints.push_back(getCoordinates(seed)); + *seed |= 0x8; + } + + walker = seed + m_displaceVector[displ]; + prewalker = displ ^ 0x7; + + while ((walker != seed) || (preseed != prewalker)) { + assert(prewalker >= 0 && prewalker < 8); + displ = NextPointTable[(neighboursCode(walker) << 3) | prewalker]; + assert(displ >= 0 && displ < 8); + + if ((displ == prewalker) && !((*walker) & 0x8)) { + endpoints.push_back(getCoordinates(walker)); + *walker |= 0x8; + } + previous = walker + m_displaceVector[prewalker]; + if (ConnectionTable[neighboursCode(previous)]) { + ret = true; + if (previous != seed) + eraseInk(previous); + } + walker += m_displaceVector[displ]; + prewalker = displ ^ 0x7; + } + + displ = NextPointTable[(neighboursCode(walker) << 3) | prewalker]; + + if ((displ == preseed) && !((*seed) & 0x8)) { + endpoints.push_back(getCoordinates(seed)); + *seed |= 0x8; + } + + if (ConnectionTable[neighboursCode(seed + m_displaceVector[preseed])]) { + ret = true; + eraseInk(seed + m_displaceVector[preseed]); + } + + if (ConnectionTable[neighboursCode(seed)]) { + ret = true; + eraseInk(seed); + } + + return ret; +} + +/*=============================================================================*/ + +void TAutocloser::Imp::cancelFromArray(vector &array, TPoint p, int &count) +{ + std::vector::iterator it = array.begin(); + int i = 0; + + for (; it != array.end(); ++it, i++) + if (it->first == p) { + if (!EndpointTable[neighboursCode(getPtr(p))]) { + assert(i != count); + if (i < count) + count--; + array.erase(it); + } + return; + } +} + +/*------------------------------------------------------------------------*/ +/* +int is_in_list(LIST list, UCHAR *br) +{ +POINT *aux; +aux = list.head; + +while(aux) + { + if (aux->p == br) return 1; + aux = aux->next; + } +return 0; +} +*/ + +/*=============================================================================*/ + +TAutocloser::TAutocloser(const TRasterP &r) +{ + m_imp = new Imp(r); +} + +//....................... + +TAutocloser::~TAutocloser() +{ + delete m_imp; +} + +//------------------------------------------------- + +//if this function is never used, use default values +void TAutocloser::setClosingDistance(int d) { m_imp->m_closingDistance = d; } + +//------------------------------------------------- + +int TAutocloser::getClosingDistance() const { return m_imp->m_closingDistance; } + +//------------------------------------------------- + +void TAutocloser::setSpotAngle(double degrees) +{ + if (degrees <= 0.0) + degrees = 1.0; + + m_imp->m_spotAngle = (degrees * TConsts::pi) / 360.0; +} + +//------------------------------------------------- + +double TAutocloser::getSpotAngle() const { return m_imp->m_spotAngle; } + +//------------------------------------------------- + +void TAutocloser::setInkIndex(int inkIndex) +{ + m_imp->m_inkIndex = inkIndex; +} + +//------------------------------------------------- + +int TAutocloser::getInkIndex() const +{ + return m_imp->m_inkIndex; +} + +//------------------------------------------------- + +void TAutocloser::compute(vector &closingSegmentArray) +{ + m_imp->compute(closingSegmentArray); +} +//------------------------------------------------- diff --git a/toonz/sources/common/trop/tblur.cpp b/toonz/sources/common/trop/tblur.cpp new file mode 100644 index 0000000..1fb3544 --- /dev/null +++ b/toonz/sources/common/trop/tblur.cpp @@ -0,0 +1,979 @@ + + +#include "traster.h" +#include "trop.h" +#include "tpixelgr.h" +#ifdef WIN32 +#include +#include +#endif + +namespace +{ + +#ifdef WIN32 +template +struct BlurPixel { + T b; + T g; + T r; + T m; +}; + +#else +template +struct BlurPixel { + T r; + T g; + T b; + T m; +}; + +#endif + +//=================================================================== + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define LOAD_COL_CODE \ + \ + buffer += x; \ + pix = col + by1; \ + \ + for (i = by1; i < ly + by1; i++) { \ + *pix++ = *buffer; \ + buffer += lx; \ + } \ + \ + pix += by2; \ + left_val = col[0]; \ + right_val = *(pix - 1); \ + col--; \ + \ + for (i = 0; i < brad; i++) { \ + *col-- = left_val; \ + *pix++ = right_val; \ + } + +//------------------------------------------------------------------- + +#define BLUR_CODE(round_fac, channel_type) \ + pix1 = row1; \ + pix2 = row1 - 1; \ + \ + sigma1.r = pix1->r; \ + sigma1.g = pix1->g; \ + sigma1.b = pix1->b; \ + sigma1.m = pix1->m; \ + pix1++; \ + \ + sigma2.r = sigma2.g = sigma2.b = sigma2.m = 0.0; \ + sigma3.r = sigma3.g = sigma3.b = sigma3.m = 0.0; \ + \ + for (i = 1; i < brad; i++) { \ + sigma1.r += pix1->r; \ + sigma1.g += pix1->g; \ + sigma1.b += pix1->b; \ + sigma1.m += pix1->m; \ + \ + sigma2.r += pix2->r; \ + sigma2.g += pix2->g; \ + sigma2.b += pix2->b; \ + sigma2.m += pix2->m; \ + \ + sigma3.r += i * (pix1->r + pix2->r); \ + sigma3.g += i * (pix1->g + pix2->g); \ + sigma3.b += i * (pix1->b + pix2->b); \ + sigma3.m += i * (pix1->m + pix2->m); \ + \ + pix1++; \ + pix2--; \ + } \ + \ + rsum = (sigma1.r + sigma2.r) * coeff - sigma3.r * coeffq + (round_fac); \ + gsum = (sigma1.g + sigma2.g) * coeff - sigma3.g * coeffq + (round_fac); \ + bsum = (sigma1.b + sigma2.b) * coeff - sigma3.b * coeffq + (round_fac); \ + msum = (sigma1.m + sigma2.m) * coeff - sigma3.m * coeffq + (round_fac); \ + \ + row2->r = (channel_type)(rsum); \ + row2->g = (channel_type)(gsum); \ + row2->b = (channel_type)(bsum); \ + row2->m = (channel_type)(msum); \ + row2++; \ + \ + sigma2.r += row1[-brad].r; \ + sigma2.g += row1[-brad].g; \ + sigma2.b += row1[-brad].b; \ + sigma2.m += row1[-brad].m; \ + \ + pix1 = row1 + brad; \ + pix2 = row1; \ + pix3 = row1 - brad; \ + pix4 = row1 - brad + 1; \ + \ + desigma.r = sigma1.r - sigma2.r; \ + desigma.g = sigma1.g - sigma2.g; \ + desigma.b = sigma1.b - sigma2.b; \ + desigma.m = sigma1.m - sigma2.m; \ + \ + for (i = 1; i < length; i++) { \ + desigma.r += pix1->r - 2 * pix2->r + pix3->r; \ + desigma.g += pix1->g - 2 * pix2->g + pix3->g; \ + desigma.b += pix1->b - 2 * pix2->b + pix3->b; \ + desigma.m += pix1->m - 2 * pix2->m + pix3->m; \ + \ + rsum += (desigma.r + diff * (pix1->r - pix4->r)) * coeffq; \ + gsum += (desigma.g + diff * (pix1->g - pix4->g)) * coeffq; \ + bsum += (desigma.b + diff * (pix1->b - pix4->b)) * coeffq; \ + msum += (desigma.m + diff * (pix1->m - pix4->m)) * coeffq; \ + \ + row2->r = (channel_type)(rsum); \ + row2->g = (channel_type)(gsum); \ + row2->b = (channel_type)(bsum); \ + row2->m = (channel_type)(msum); \ + row2++; \ + pix1++, pix2++, pix3++, pix4++; \ + } + +//------------------------------------------------------------------- + +template +inline void blur_code( + PIXEL_SRC *row1, PIXEL_DST *row2, + int length, + float coeff, float coeffq, + int brad, float diff, + float round_fac) +{ + int i; + T rsum, gsum, bsum, msum; + + BlurPixel sigma1, sigma2, sigma3, desigma; + PIXEL_SRC *pix1, *pix2, *pix3, *pix4; + + pix1 = row1; + pix2 = row1 - 1; + + sigma1.r = pix1->r; + sigma1.g = pix1->g; + sigma1.b = pix1->b; + sigma1.m = pix1->m; + pix1++; + + sigma2.r = sigma2.g = sigma2.b = sigma2.m = 0.0; + sigma3.r = sigma3.g = sigma3.b = sigma3.m = 0.0; + + for (i = 1; i < brad; i++) { + sigma1.r += pix1->r; + sigma1.g += pix1->g; + sigma1.b += pix1->b; + sigma1.m += pix1->m; + + sigma2.r += pix2->r; + sigma2.g += pix2->g; + sigma2.b += pix2->b; + sigma2.m += pix2->m; + + sigma3.r += i * (pix1->r + pix2->r); + sigma3.g += i * (pix1->g + pix2->g); + sigma3.b += i * (pix1->b + pix2->b); + sigma3.m += i * (pix1->m + pix2->m); + + pix1++; + pix2--; + } + + rsum = (sigma1.r + sigma2.r) * coeff - sigma3.r * coeffq + (round_fac); + gsum = (sigma1.g + sigma2.g) * coeff - sigma3.g * coeffq + (round_fac); + bsum = (sigma1.b + sigma2.b) * coeff - sigma3.b * coeffq + (round_fac); + msum = (sigma1.m + sigma2.m) * coeff - sigma3.m * coeffq + (round_fac); + + row2->r = rsum; + row2->g = gsum; + row2->b = bsum; + row2->m = msum; + row2++; + + sigma2.r += row1[-brad].r; + sigma2.g += row1[-brad].g; + sigma2.b += row1[-brad].b; + sigma2.m += row1[-brad].m; + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma.r = sigma1.r - sigma2.r; + desigma.g = sigma1.g - sigma2.g; + desigma.b = sigma1.b - sigma2.b; + desigma.m = sigma1.m - sigma2.m; + + for (i = 1; i < length; i++) { + desigma.r += pix1->r - 2 * pix2->r + pix3->r; + desigma.g += pix1->g - 2 * pix2->g + pix3->g; + desigma.b += pix1->b - 2 * pix2->b + pix3->b; + desigma.m += pix1->m - 2 * pix2->m + pix3->m; + + rsum += (desigma.r + diff * (pix1->r - pix4->r)) * coeffq; + gsum += (desigma.g + diff * (pix1->g - pix4->g)) * coeffq; + bsum += (desigma.b + diff * (pix1->b - pix4->b)) * coeffq; + msum += (desigma.m + diff * (pix1->m - pix4->m)) * coeffq; + + row2->r = rsum; + row2->g = gsum; + row2->b = bsum; + row2->m = msum; + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + +//------------------------------------------------------------------- + +#ifdef WIN32 + +//------------------------------------------------------------------- +template +inline void blur_code_SSE2( + T *row1, BlurPixel

    *row2, + int length, + float coeff, float coeffq, + int brad, float diff, + float round_fac) +{ + + static float two = 2; + static __m128i zeros = _mm_setzero_si128(); + static __m128 twos = _mm_load_ps1(&two); + + int i; + + __m128 sigma1, sigma2, sigma3, desigma; + T *pix1, *pix2, *pix3, *pix4; + + pix1 = row1; + pix2 = row1 - 1; + + // + __m128i piPix1 = _mm_cvtsi32_si128(*(DWORD *)pix1); + __m128i piPix2 = _mm_cvtsi32_si128(*(DWORD *)pix2); + + piPix1 = _mm_unpacklo_epi8(piPix1, zeros); + piPix2 = _mm_unpacklo_epi8(piPix2, zeros); + piPix1 = _mm_unpacklo_epi16(piPix1, zeros); + piPix2 = _mm_unpacklo_epi16(piPix2, zeros); + + sigma1 = _mm_cvtepi32_ps(piPix1); + // + + pix1++; + + float zero = 0; + sigma2 = _mm_load1_ps(&zero); + sigma3 = _mm_load1_ps(&zero); + + for (i = 1; i < brad; i++) { + piPix1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix1), zeros); + piPix2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix2), zeros); + + __m128 pPix1 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPix1, zeros)); + __m128 pPix2 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPix2, zeros)); + + sigma1 = _mm_add_ps(sigma1, pPix1); + sigma2 = _mm_add_ps(sigma2, pPix2); + + __m128i pii = _mm_unpacklo_epi8(_mm_cvtsi32_si128(i), zeros); + __m128 pi = _mm_cvtepi32_ps(_mm_unpacklo_epi16(pii, zeros)); + + pPix1 = _mm_add_ps(pPix1, pPix2); + pPix1 = _mm_mul_ps(pi, pPix1); // i*(pix1 + pix2) + sigma3 = _mm_add_ps(sigma3, pPix1); // sigma3 += i*(pix1 + pix2) + + pix1++; + pix2--; + } + + __m128 pCoeff = _mm_load1_ps(&coeff); + __m128 pCoeffq = _mm_load1_ps(&coeffq); + __m128 pRoundFac = _mm_load1_ps(&round_fac); + __m128 pDiff = _mm_load1_ps(&diff); + + // sum = (sigma1 + sigma2)*coeff - sigma3*coeffq + round_fac + __m128 sum = _mm_add_ps(sigma1, sigma2); + sum = _mm_mul_ps(sum, pCoeff); + __m128 sum2 = _mm_mul_ps(sigma3, pCoeffq); + sum2 = _mm_add_ps(sum2, pRoundFac); + sum = _mm_sub_ps(sum, sum2); + /* + __m128i isum = _mm_cvtps_epi32(sum); + isum = _mm_packs_epi32(isum, zeros); + isum = _mm_packs_epi16(isum, zeros); + + *(DWORD*)row2 = _mm_cvtsi128_si32(isum); +*/ + _mm_store_ps((float *)row2, sum); + row2++; + + __m128i piPixMin = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)(row1 - brad)), zeros); + __m128 pPixMin = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPixMin, zeros)); + sigma2 = _mm_add_ps(sigma2, pPixMin); + /* + sigma2.r += row1[-brad].r; + sigma2.g += row1[-brad].g; + sigma2.b += row1[-brad].b; + sigma2.m += row1[-brad].m; +*/ + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma = _mm_sub_ps(sigma1, sigma2); + + for (i = 1; i < length; i++) { + piPix1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix1), zeros); + piPix2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix2), zeros); + __m128i piPix3 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix3), zeros); + __m128i piPix4 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)pix4), zeros); + + __m128 pPix1 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPix1, zeros)); + __m128 pPix2 = _mm_cvtepi32_ps(_mm_slli_epi32(_mm_unpacklo_epi16(piPix2, zeros), 1)); + __m128 pPix3 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPix3, zeros)); + __m128 pPix4 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(piPix4, zeros)); + + // desigma += pix1 - 2*pix2 + pix3 + __m128 tmp = _mm_sub_ps(pPix3, pPix2); + tmp = _mm_add_ps(tmp, pPix1); + desigma = _mm_add_ps(desigma, tmp); + + // sum += (desigma + diff*(pix1 - pix4))*coeffq + tmp = _mm_sub_ps(pPix1, pPix4); + tmp = _mm_mul_ps(tmp, pDiff); + tmp = _mm_add_ps(desigma, tmp); + tmp = _mm_mul_ps(tmp, pCoeffq); + sum = _mm_add_ps(sum, tmp); + /* + isum = _mm_cvtps_epi32(sum); + isum = _mm_packs_epi32(isum, zeros); + isum = _mm_packs_epi16(isum, zeros); + + *(DWORD*)row2 = _mm_cvtsi128_si32(isum); +*/ + _mm_store_ps((float *)row2, sum); + + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + +//------------------------------------------------------------------- +template +inline void blur_code_SSE2( + BlurPixel

    *row1, T *row2, + int length, + float coeff, float coeffq, + int brad, float diff, + float round_fac) +{ + int i; + + float two = 2; + __m128i zeros = _mm_setzero_si128(); + __m128 twos = _mm_load_ps1(&two); + + __m128 sigma1, sigma2, sigma3, desigma; + BlurPixel

    *pix1, *pix2, *pix3, *pix4; + + pix1 = row1; + pix2 = row1 - 1; + + __m128 pPix1 = _mm_load_ps((float *)pix1); + __m128 pPix2 = _mm_load_ps((float *)pix2); + + // sigma1 = *pix1 + sigma1 = pPix1; + + pix1++; + + float zero = 0; + sigma2 = _mm_load1_ps(&zero); + sigma3 = _mm_load1_ps(&zero); + + for (i = 1; i < brad; i++) { + pPix1 = _mm_load_ps((float *)pix1); + pPix2 = _mm_load_ps((float *)pix2); + + sigma1 = _mm_add_ps(sigma1, pPix1); + sigma2 = _mm_add_ps(sigma2, pPix2); + + __m128i pii = _mm_unpacklo_epi8(_mm_cvtsi32_si128(i), zeros); + __m128 pi = _mm_cvtepi32_ps(_mm_unpacklo_epi16(pii, zeros)); + + pPix1 = _mm_add_ps(pPix1, pPix2); + pPix1 = _mm_mul_ps(pi, pPix1); // i*(pix1 + pix2) + sigma3 = _mm_add_ps(sigma3, pPix1); // sigma3 += i*(pix1 + pix2) + + pix1++; + pix2--; + } + + __m128 pCoeff = _mm_load1_ps(&coeff); + __m128 pCoeffq = _mm_load1_ps(&coeffq); + // __m128 pRoundFac = _mm_load1_ps(&round_fac); + __m128 pDiff = _mm_load1_ps(&diff); + + // sum = (sigma1 + sigma2)*coeff - sigma3*coeffq + round_fac + __m128 sum = _mm_add_ps(sigma1, sigma2); + sum = _mm_mul_ps(sum, pCoeff); + __m128 sum2 = _mm_mul_ps(sigma3, pCoeffq); + //sum2 = _mm_add_ps(sum2, pRoundFac); + sum = _mm_sub_ps(sum, sum2); + + // converte i canali da float a char + __m128i isum = _mm_cvtps_epi32(sum); + isum = _mm_packs_epi32(isum, zeros); + //isum = _mm_packs_epi16(isum, zeros); + isum = _mm_packus_epi16(isum, zeros); + *(DWORD *)row2 = _mm_cvtsi128_si32(isum); + + row2++; + + // sigma2 += row1[-brad] + __m128 pPixMin = _mm_load_ps((float *)(row1 - brad)); + sigma2 = _mm_add_ps(sigma2, pPixMin); + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma = _mm_sub_ps(sigma1, sigma2); + + for (i = 1; i < length; i++) { + __m128 pPix1 = _mm_load_ps((float *)pix1); + __m128 pPix2 = _mm_load_ps((float *)pix2); + __m128 pPix3 = _mm_load_ps((float *)pix3); + __m128 pPix4 = _mm_load_ps((float *)pix4); + + pPix2 = _mm_mul_ps(pPix2, twos); + + // desigma += pix1 - 2*pix2 + pix3 + __m128 tmp = _mm_sub_ps(pPix3, pPix2); + tmp = _mm_add_ps(tmp, pPix1); + desigma = _mm_add_ps(desigma, tmp); + + // sum += (desigma + diff*(pix1 - pix4))*coeffq + tmp = _mm_sub_ps(pPix1, pPix4); + tmp = _mm_mul_ps(tmp, pDiff); + tmp = _mm_add_ps(desigma, tmp); + tmp = _mm_mul_ps(tmp, pCoeffq); + sum = _mm_add_ps(sum, tmp); + + // converte i canali da float a char + __m128i isum = _mm_cvtps_epi32(sum); + isum = _mm_packs_epi32(isum, zeros); + //isum = _mm_packs_epi16(isum, zeros); // QUESTA RIGA E' SBAGLIATA + //assert(false); + isum = _mm_packus_epi16(isum, zeros); + *(DWORD *)row2 = _mm_cvtsi128_si32(isum); + + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + +#endif // WIN32 + +//------------------------------------------------------------------- + +#define STORE_COL_CODE(crop_val) \ + { \ + int i, val; \ + double ampl; \ + buffer += x; \ + \ + ampl = 1.0 + blur / 15.0; \ + \ + if (backlit) \ + for (i = ((dy >= 0) ? 0 : -dy); i < MIN(ly, r_ly - dy); i++) { \ + val = troundp(col[i].r * ampl); \ + buffer->r = (val > crop_val) ? crop_val : val; \ + val = troundp(col[i].g * ampl); \ + buffer->g = (val > crop_val) ? crop_val : val; \ + val = troundp(col[i].b * ampl); \ + buffer->b = (val > crop_val) ? crop_val : val; \ + val = troundp(col[i].m * ampl); \ + buffer->m = (val > crop_val) ? crop_val : val; \ + buffer += wrap; \ + } \ + else \ + for (i = ((dy >= 0) ? 0 : -dy); i < MIN(ly, r_ly - dy); i++) { \ + *buffer = col[i]; \ + buffer += wrap; \ + } \ + } + +//------------------------------------------------------------------- +template +void store_colRgb(T *buffer, int wrap, int r_ly, T *col, + int ly, int x, int dy, int backlit, double blur) +{ + int val = T::maxChannelValue; + + if (val == 255) + STORE_COL_CODE(204) + else if (val == 65535) + STORE_COL_CODE(204 * 257) + else + assert(false); +} + +//------------------------------------------------------------------- +template +void store_colGray(T *buffer, int wrap, int r_ly, T *col, + int ly, int x, int dy, int backlit, double blur) +{ + int i; + double ampl; + buffer += x; + + ampl = 1.0 + blur / 15.0; + + for (i = ((dy >= 0) ? 0 : -dy); i < MIN(ly, r_ly - dy); i++) { + *buffer = col[i]; + buffer += wrap; + } +} + +//------------------------------------------------------------------- +template +void load_colRgb(BlurPixel

    *buffer, BlurPixel

    *col, + int lx, int ly, int x, int brad, int by1, int by2) +{ + int i; + BlurPixel

    *pix, left_val, right_val; + + LOAD_COL_CODE +} + +//------------------------------------------------------------------- + +void load_channel_col32(float *buffer, float *col, + int lx, int ly, int x, int brad, int by1, int by2) +{ + int i; + float *pix, left_val, right_val; + + LOAD_COL_CODE +} + +//------------------------------------------------------------------- +template +void do_filtering_chan(BlurPixel

    *row1, T *row2, int length, + float coeff, float coeffq, int brad, float diff, bool useSSE) +{ +#ifdef WIN32 + if (useSSE && T::maxChannelValue == 255) + blur_code_SSE2(row1, row2, length, coeff, coeffq, brad, diff, 0.5); + else +#endif + { + int i; + P rsum, gsum, bsum, msum; + BlurPixel

    sigma1, sigma2, sigma3, desigma; + BlurPixel

    *pix1, *pix2, *pix3, *pix4; + + BLUR_CODE((P)0.5, Q) + } +} + +//------------------------------------------------------------------- + +template +void do_filtering_channel_float(T *row1, float *row2, + int length, + float coeff, float coeffq, + int brad, float diff) +{ + int i; + float sum; + float sigma1, sigma2, sigma3, desigma; + T *pix1, *pix2, *pix3, *pix4; + + pix1 = row1; + pix2 = row1 - 1; + + sigma1 = pix1->value; + pix1++; + + sigma2 = 0.0; + sigma3 = 0.0; + + for (i = 1; i < brad; i++) { + sigma1 += pix1->value; + sigma2 += pix2->value; + sigma3 += i * (pix1->value + pix2->value); + pix1++; + pix2--; + } + + sum = (sigma1 + sigma2) * coeff - sigma3 * coeffq; + + *row2 = sum; + row2++; + + sigma2 += row1[-brad].value; + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma = sigma1 - sigma2; + + for (i = 1; i < length; i++) { + desigma += pix1->value - 2 * pix2->value + pix3->value; + + sum += (desigma + diff * (pix1->value - pix4->value)) * coeffq; + + *row2 = sum; + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + +//------------------------------------------------------------------- +template +void do_filtering_channel_gray(float *row1, T *row2, int length, + float coeff, float coeffq, int brad, float diff) +{ + int i; + float sum; + float sigma1, sigma2, sigma3, desigma; + float *pix1, *pix2, *pix3, *pix4; + + pix1 = row1; + pix2 = row1 - 1; + + sigma1 = *pix1; + pix1++; + + sigma2 = 0.0; + sigma3 = 0.0; + + for (i = 1; i < brad; i++) { + sigma1 += *pix1; + sigma2 += *pix2; + sigma3 += i * (*pix1 + *pix2); + pix1++; + pix2--; + } + + sum = (sigma1 + sigma2) * coeff - sigma3 * coeffq + 0.5F; + + row2->setValue((int)sum); + row2++; + + sigma2 += row1[-brad]; + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma = sigma1 - sigma2; + + for (i = 1; i < length; i++) { + desigma += *pix1 - 2 * (*pix2) + (*pix3); + + sum += (desigma + diff * (*pix1 - *pix4)) * coeffq; + + row2->setValue((int)sum); + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + +//------------------------------------------------------------------- +template +void load_rowRgb(TRasterPT &rin, T *row, int lx, int y, int brad, + int bx1, int bx2) +{ + int i; + T *buf32, *pix; + T left_val, right_val; + + pix = row + bx1; + + { + rin->lock(); + buf32 = rin->pixels(y); + + for (i = 0; i < lx; i++) + *pix++ = *buf32++; + rin->unlock(); + } + + pix += bx2; + left_val = *row; + right_val = *(pix - 1); + row--; + + for (i = 0; i < brad; i++) /* pixels equal to the ones of border of image are added */ + { /* to avoid a black blur to get into the picture. */ + *row-- = left_val; + *pix++ = right_val; + } +} + +//------------------------------------------------------------------- +template +void load_rowGray(TRasterPT &rin, T *row, int lx, int y, int brad, + int bx1, int bx2) +{ + int i; + T *buf8, *pix; + T left_val, right_val; + + pix = row + bx1; + buf8 = (T *)(rin->pixels(y)); + + for (i = 0; i < lx; i++) + *pix++ = *buf8++; + + pix += bx2; + left_val = *row; + right_val = *(pix - 1); + row--; + + for (i = 0; i < brad; i++) /* pixels equal to the ones of border of image are added */ + { /* to avoid a black blur to get into the picture. */ + *row-- = left_val; + *pix++ = right_val; + } +} + +//------------------------------------------------------------------- +template +void do_filtering_floatRgb(T *row1, BlurPixel

    *row2, int length, + float coeff, float coeffq, int brad, float diff, bool useSSE) +{ +/* + int i; + float rsum, gsum, bsum, msum; + CASM_FPIXEL sigma1, sigma2, sigma3, desigma; + TPixel32 *pix1, *pix2, *pix3, *pix4; + + BLUR_CODE(0, unsigned char) +*/ + +#ifdef WIN32 + if (useSSE) + blur_code_SSE2(row1, row2, length, coeff, coeffq, brad, diff, 0); + else +#endif + blur_code, P>(row1, row2, length, coeff, coeffq, brad, diff, 0); +} + +//------------------------------------------------------------------- +template +void doBlurRgb(TRasterPT &dstRas, TRasterPT &srcRas, double blur, int dx, int dy, bool useSSE) +{ + int i, lx, ly, llx, lly, brad; + float coeff, coeffq, diff; + int bx1 = 0, by1 = 0, bx2 = 0, by2 = 0; + + brad = (int)ceil(blur); /* number of pixels involved in the filtering */ + + //int border = brad*2; // per sicurezza + + coeff = (float)(blur / (brad - brad * brad + blur * (2 * brad - 1))); /*sum of the weights of triangolar filter. */ + coeffq = (float)(coeff / blur); + diff = (float)(blur - brad); + lx = srcRas->getLx(); + ly = srcRas->getLy(); + + if ((lx == 0) || (ly == 0)) + return; + + llx = lx + bx1 + bx2; + lly = ly + by1 + by2; + + T *row1, *col2, *buffer; + BlurPixel

    *row2, *col1, *fbuffer; + TRasterGR8P r1; + +#ifdef WIN32 + if (useSSE) { + fbuffer = (BlurPixel

    *)_aligned_malloc(llx * ly * sizeof(BlurPixel

    ), 16); + row1 = (T *)_aligned_malloc((llx + 2 * brad) * sizeof(T), 16); + col1 = (BlurPixel

    *)_aligned_malloc((lly + 2 * brad) * sizeof(BlurPixel

    ), 16); + col2 = (T *)_aligned_malloc(lly * sizeof(T), 16); + } else +#endif + { + TRasterGR8P raux(llx * sizeof(BlurPixel

    ), ly); + r1 = raux; + r1->lock(); + fbuffer = (BlurPixel

    *)r1->getRawData(); //new CASM_FPIXEL [llx *ly]; + row1 = new T[llx + 2 * brad]; + col1 = new BlurPixel

    [lly + 2 * brad]; + col2 = new T[lly]; + } + + if ((!fbuffer) || (!row1) || (!col1) || (!col2)) { + if (!useSSE) + r1->unlock(); + return; + } + + row2 = fbuffer; + + try { + for (i = 0; i < ly; i++) { + load_rowRgb(srcRas, row1 + brad, lx, i, brad, bx1, bx2); + do_filtering_floatRgb(row1 + brad, row2, llx, coeff, coeffq, brad, diff, useSSE); + row2 += llx; + } + dstRas->lock(); + buffer = (T *)dstRas->getRawData(); + + if (dy >= 0) + buffer += (dstRas->getWrap()) * dy; + + for (i = (dx >= 0) ? 0 : -dx; i < tmin(llx, dstRas->getLx() - dx); i++) { + load_colRgb

    (fbuffer, col1 + brad, llx, ly, i, brad, by1, by2); + do_filtering_chan(col1 + brad, col2, lly, coeff, coeffq, brad, diff, useSSE); + store_colRgb(buffer, dstRas->getWrap(), dstRas->getLy(), col2, lly, i + dx, dy, 0, blur); + } + dstRas->unlock(); + } catch (...) { + dstRas->clear(); + } + +#ifdef WIN32 + if (useSSE) { + _aligned_free(col2); + _aligned_free(col1); + _aligned_free(row1); + _aligned_free(fbuffer); + } else +#endif + { + delete[] col2; + delete[] col1; + delete[] row1; + r1->unlock(); + //delete[]fbuffer; + } +} + +//------------------------------------------------------------------- + +template +void doBlurGray(TRasterPT &dstRas, TRasterPT &srcRas, double blur, int dx, int dy) +{ + int i, lx, ly, llx, lly, brad; + float coeff, coeffq, diff; + int bx1 = 0, by1 = 0, bx2 = 0, by2 = 0; + + brad = (int)ceil(blur); /* number of pixels involved in the filtering */ + coeff = (float)(blur / (brad - brad * brad + blur * (2 * brad - 1))); /*sum of the weights of triangolar filter. */ + coeffq = (float)(coeff / blur); + diff = (float)(blur - brad); + lx = srcRas->getLx(); + ly = srcRas->getLy(); + + if ((lx == 0) || (ly == 0)) + return; + + llx = lx + bx1 + bx2; + lly = ly + by1 + by2; + + T *row1, *col2, *buffer; + float *row2, *col1, *fbuffer; + + TRasterGR8P r1(llx * sizeof(float), ly); + r1->lock(); + fbuffer = (float *)r1->getRawData(); //new float[llx *ly]; + + row1 = new T[llx + 2 * brad]; + col1 = new float[lly + 2 * brad]; + col2 = new T[lly]; + + if ((!fbuffer) || (!row1) || (!col1) || (!col2)) + return; + + row2 = fbuffer; + + for (i = 0; i < ly; i++) { + load_rowGray(srcRas, row1 + brad, lx, i, brad, bx1, bx2); + do_filtering_channel_float(row1 + brad, row2, llx, coeff, coeffq, brad, diff); + row2 += llx; + } + dstRas->lock(); + buffer = (T *)dstRas->getRawData(); + + if (dy >= 0) + buffer += (dstRas->getWrap()) * dy; + + for (i = (dx >= 0) ? 0 : -dx; i < tmin(llx, dstRas->getLx() - dx); i++) { + load_channel_col32(fbuffer, col1 + brad, llx, ly, i, brad, by1, by2); + do_filtering_channel_gray(col1 + brad, col2, lly, coeff, coeffq, brad, diff); + + int backlit = 0; + store_colGray(buffer, dstRas->getWrap(), dstRas->getLy(), col2, lly, i + dx, dy, backlit, blur); + } + dstRas->unlock(); + delete[] col2; + delete[] col1; + delete[] row1; + r1->unlock(); //delete[]fbuffer; +} + +}; // namespace + +//==================================================================== + +int TRop::getBlurBorder(double blur) +{ + int brad = (int)ceil(blur); /* number of pixels involved in the filtering */ + + int border = brad * 2; // per sicurezza + return border; +} + +//-------------------------------------------------------------------- + +void TRop::blur(const TRasterP &dstRas, const TRasterP &srcRas, double blur, int dx, int dy, bool useSSE) +{ + TRaster32P dstRas32 = dstRas; + TRaster32P srcRas32 = srcRas; + + if (dstRas32 && srcRas32) + doBlurRgb(dstRas32, srcRas32, blur, dx, dy, useSSE); + else { + TRaster64P dstRas64 = dstRas; + TRaster64P srcRas64 = srcRas; + if (dstRas64 && srcRas64) + doBlurRgb(dstRas64, srcRas64, blur, dx, dy, useSSE); + else { + TRasterGR8P dstRasGR8 = dstRas; + TRasterGR8P srcRasGR8 = srcRas; + + if (dstRasGR8 && srcRasGR8) + doBlurGray(dstRasGR8, srcRasGR8, blur, dx, dy); + else { + TRasterGR16P dstRasGR16 = dstRas; + TRasterGR16P srcRasGR16 = srcRas; + + if (dstRasGR16 && srcRasGR16) + doBlurGray(dstRasGR16, srcRasGR16, blur, dx, dy); + else + throw TException("TRop::blur unsupported pixel type"); + } + } + } +} diff --git a/toonz/sources/common/trop/tcheckboard.cpp b/toonz/sources/common/trop/tcheckboard.cpp new file mode 100644 index 0000000..c1b831e --- /dev/null +++ b/toonz/sources/common/trop/tcheckboard.cpp @@ -0,0 +1,96 @@ + + +#include "trop.h" +#include "tpixelutils.h" + +#define FLOOR(x) ((int)(x) > (x) ? (int)(x)-1 : (int)(x)) + +/* +inline bool isInFirstColor(const TDimensionD &dim,int x, int y,const TPointD &offset) +{ + int lx=dim.lx; + int ly=dim.ly; + int offX,offY; + offX = offset.x>=0 ? (int)offset.x : (int)(lx-offset.x); + offY = offset.y>=0 ? (int)offset.y : (int)(ly-offset.y); + if ((( ((int)(offX+x))/lx + ((int)(offY+y))/ly )%2 ) == 0) + return false; + return true; +} +*/ + +//----------------------------------------------------------------------------- + +namespace +{ + +template +void do_checkBoard(TRasterPT rout, const PIXEL &pix1, const PIXEL &pix2, + const TDimensionD &dim, const TPointD &offset) +{ + assert(dim.lx > 0); + assert(dim.ly > 0); + + double freqX = 0.5 / dim.lx; + double freqY = 0.5 / dim.ly; + + double phaseX = 0; + if (offset.x >= 0) { + double q = offset.x * freqX; + phaseX = (q - floor(q)); + } else { + double q = (-offset.x * freqX); + phaseX = 1.0 - (q - floor(q)); + } + + double phaseY = 0; + if (offset.y >= 0) { + double q = offset.y * freqY; + phaseY = (q - floor(q)); + } else { + double q = (-offset.y * freqY); + phaseY = 1.0 - (q - floor(q)); + } + + int lx = rout->getLx(); + int ly = rout->getLy(); + + for (int y = 0; y < ly; y++) { + double yy = 2.0 * (phaseY + y * freqY); + int iy = FLOOR(yy); + assert(iy == (int)floor(yy)); + rout->lock(); + PIXEL *pix = rout->pixels(y); + for (int x = 0; x < lx; x++) { + double xx = 2.0 * (phaseX + x * freqX); + int ix = FLOOR(xx); + assert(ix == (int)floor(xx)); + if ((ix ^ iy) & 1) + *pix++ = pix1; + else + *pix++ = pix2; + } + rout->unlock(); + } +} + +} //namespace + +//----------------------------------------------------------------------------- + +void TRop::checkBoard(TRasterP rout, const TPixel32 &pix1, const TPixel32 &pix2, + const TDimensionD &dim, const TPointD &offset) +{ + //assert(offset.x<=dim.lx && offset.y<=dim.ly); + + TRaster32P rout32 = rout; + if (rout32) + do_checkBoard(rout32, pix1, pix2, dim, offset); + else { + TRaster64P rout64 = rout; + if (rout64) + do_checkBoard(rout64, toPixel64(pix1), toPixel64(pix2), dim, offset); + else + throw TRopException("unsupported pixel type"); + } +} diff --git a/toonz/sources/common/trop/tconvert.cpp b/toonz/sources/common/trop/tconvert.cpp new file mode 100644 index 0000000..9d3b749 --- /dev/null +++ b/toonz/sources/common/trop/tconvert.cpp @@ -0,0 +1,410 @@ + + +#include "trop.h" + +// TnzCore includes +#include "tpixelgr.h" +#include "trandom.h" +#include "tpixelutils.h" + +//****************************************************************** +// Conversion functions +//****************************************************************** + +void do_convert(const TRaster64P &dst, const TRaster32P &src) +{ + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixel64 *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y); + TPixel32 *inEndPix = inPix + lx; + for (; inPix < inEndPix; ++outPix, ++inPix) { + outPix->r = ushortFromByte(inPix->r); + outPix->g = ushortFromByte(inPix->g); + outPix->b = ushortFromByte(inPix->b); + outPix->m = ushortFromByte(inPix->m); + } + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRasterGR8P &dst, const TRaster32P &src) +{ + assert(dst->getSize() == src->getSize()); + + int lx = src->getLx(); + + for (int y = 0; y < src->getLy(); ++y) { + TPixelGR8 *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y), *inEndPix = inPix + lx; + + for (; inPix < inEndPix; ++outPix, ++inPix) + *outPix = TPixelGR8::from(overPix(TPixel32::White, *inPix)); + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRasterGR16P &dst, const TRaster32P &src) +{ + assert(dst->getSize() == src->getSize()); + + int lx = src->getLx(); + + for (int y = 0; y < src->getLy(); ++y) { + TPixelGR16 *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y), *inEndPix = inPix + lx; + + for (; inPix < inEndPix; ++outPix, ++inPix) + outPix->value = 257 * (TPixelGR8::from(overPix(TPixel32::White, *inPix))).value; + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRasterGR16P &dst, const TRaster64P &src) +{ + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixelGR16 *outPix = dst->pixels(y); + TPixel64 *inPix = src->pixels(y); + TPixel64 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + outPix->value = (inPix->r + 2 * inPix->g + inPix->b) >> 2; + outPix++; + inPix++; + } + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRaster32P &dst, const TRasterGR8P &src) +{ + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixel32 *outPix = dst->pixels(y); + TPixelGR8 *inPix = src->pixels(y); + TPixelGR8 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + outPix->r = inPix->value; + outPix->g = inPix->value; + outPix->b = inPix->value; + outPix->m = 0xff; + outPix++; + inPix++; + } + } +} + +//----------------------------------------------------------------------------- + +#define USHORT2BYTE_MAGICFAC (256U * 255U + 1U) + +inline UCHAR ditherUcharFromUshort(USHORT in, UINT rndNum) +{ + return ((((in * USHORT2BYTE_MAGICFAC) - ((in * USHORT2BYTE_MAGICFAC) >> 24)) + rndNum) >> 24); +} + +inline void ditherRgbmFromRgbm64(TPixel32 &out, const TPixel64 &in, TRandom &rnd) +{ + UINT randomRound; + randomRound = rnd.getUInt() & ((1U << 24) - 1); + + out.r = ditherUcharFromUshort(in.r, randomRound); + out.g = ditherUcharFromUshort(in.g, randomRound); + out.b = ditherUcharFromUshort(in.b, randomRound); + out.m = ditherUcharFromUshort(in.m, randomRound); +} + +//----------------------------------------------------------------------------- + +inline void ditherConvert(TRaster64P inRas, TRaster32P outRas) +{ + int inWrap = inRas->getWrap(); + int outWrap = outRas->getWrap(); + + TPixel64 *inPix = 0, *inRow = inRas->pixels(); + TPixel32 *outPix, *outRow = outRas->pixels(); + TPixel64 *endPix; + int inLx = inRas->getLx(); + TPixel64 *lastPix = inRow + inWrap * (inRas->getLy() - 1) + inLx; + + TRandom rnd(130266); + + while (inPix < lastPix) { + inPix = inRow; + outPix = outRow; + endPix = inPix + inLx; + while (inPix < endPix) { + ditherRgbmFromRgbm64(*outPix, *inPix, rnd); + inPix++; + outPix++; + } + inRow += inWrap; + outRow += outWrap; + } +} + +//****************************************************************** +// Obsolete conversion functions +//****************************************************************** + +void do_convert(const TRasterCM32P &dst, const TRasterGR8P &src) +{ + assert(dst->getSize() == src->getSize()); + TPixelCM32 bg = TPixelCM32(0, 0, TPixelCM32::getMaxTone()); + + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixelCM32 *outPix = dst->pixels(y); + TPixelGR8 *inPix = src->pixels(y); + TPixelGR8 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + *outPix = (inPix->value == 255) ? bg : TPixelCM32(1, 0, inPix->value); + outPix++; + inPix++; + } + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRasterCM32P &dst, const TRaster32P &src) +{ + assert(dst->getSize() == src->getSize()); + TPixelCM32 bg = TPixelCM32(0, 0, TPixelCM32::getMaxTone()); + + int lx = src->getLx(); + + bool isOverlay = false; + + for (int y = 0; y < src->getLy(); y++) //if it is an overlay, I use the matte value for inks, otherwise I use the brightness. + { + TPixel32 *inPix = src->pixels(y); + TPixel32 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + if (inPix->m != 255) { + isOverlay = true; + break; + } + inPix++; + } + if (isOverlay) + break; + } + + if (isOverlay) + for (int y = 0; y < src->getLy(); y++) { + TPixelCM32 *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y); + TPixel32 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + *outPix = (inPix->m == 0) ? bg : TPixelCM32(1, 0, 255 - inPix->m); + outPix++; + inPix++; + } + } + else + for (int y = 0; y < src->getLy(); y++) { + TPixelCM32 *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y); + TPixel32 *inEndPix = inPix + lx; + while (inPix < inEndPix) { + UCHAR val = TPixelGR8::from(*inPix).value; + *outPix = (val == 255) ? bg : TPixelCM32(1, 0, val); + outPix++; + inPix++; + } + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRasterYUV422P &dst, const TRaster32P &src) +{ + assert(src->getLx() & 0); + long y1, y2, u, v, u1, u2, v1, v2; + TPixel32 *pix = (TPixel32 *)src->pixels(); + TPixel32 *lastPix = &(src->pixels(src->getLy() - 1)[src->getLx() - 1]); + + UCHAR *out = dst->getRawData(); + + while (pix < lastPix) { + /* first pixel gives Y and 0.5 of chroma */ + + y1 = 16829 * pix->r + 33039 * pix->g + 6416 * pix->b; + u1 = -4831 * pix->r + -9488 * pix->g + 14319 * pix->b; + v1 = 14322 * pix->r + -11992 * pix->g + -2330 * pix->b; + + /* second pixel gives Y and 0.5 of chroma */ + ++pix; + + y2 = 16829 * pix->r + 33039 * pix->g + 6416 * pix->b; + u2 = -4831 * pix->r + -9488 * pix->g + 14319 * pix->b; + v2 = 14322 * pix->r + -11992 * pix->g + -2330 * pix->b; + + /* average the chroma */ + u = u1 + u2; + v = v1 + v2; + + /* round the chroma */ + u1 = (u + 0x008000) >> 16; + v1 = (v + 0x008000) >> 16; + + /* limit the chroma */ + if (u1 < -112) + u1 = -112; + if (u1 > 111) + u1 = 111; + if (v1 < -112) + v1 = -112; + if (v1 > 111) + v1 = 111; + + /* limit the lum */ + if (y1 > 0x00dbffff) + y1 = 0x00dbffff; + if (y2 > 0x00dbffff) + y2 = 0x00dbffff; + + /* save the results */ + *out++ = (UCHAR)(u1 + 128); + *out++ = (UCHAR)((y1 >> 16) + 16); + *out++ = (UCHAR)(v1 + 128); + *out++ = (UCHAR)((y2 >> 16) + 16); + ++pix; + } +} + +//----------------------------------------------------------------------------- + +void do_convert(const TRaster32P &dst, const TRasterYUV422P &src) +{ + int long r, g, b, y1, y2, u, v; + TPixel32 *buf = dst->pixels(); + const UCHAR *in = src->getRawData(); + const UCHAR *last = in + src->getRowSize() * src->getLy() - 1; + + while (in < last) { + u = *in; + u -= 128; + in++; + y1 = *in; + y1 -= 16; + in++; + v = *in; + v -= 128; + in++; + y2 = *in; + y2 -= 16; + in++; + + r = 76310 * y1 + 104635 * v; + if (r > 0xFFFFFF) + r = 0xFFFFFF; + if (r <= 0xFFFF) + r = 0; + + g = 76310 * y1 + -25690 * u + -53294 * v; + if (g > 0xFFFFFF) + g = 0xFFFFFF; + if (g <= 0xFFFF) + g = 0; + + b = 76310 * y1 + 132278 * u; + if (b > 0xFFFFFF) + b = 0xFFFFFF; + if (b <= 0xFFFF) + b = 0; + + buf->r = (UCHAR)(r >> 16); + buf->g = (UCHAR)(g >> 16); + buf->b = (UCHAR)(b >> 16); + buf->m = (UCHAR)255; + buf++; + + r = 76310 * y2 + 104635 * v; + if (r > 0xFFFFFF) + r = 0xFFFFFF; + if (r <= 0xFFFF) + r = 0; + + g = 76310 * y2 + -25690 * u + -53294 * v; + if (g > 0xFFFFFF) + g = 0xFFFFFF; + if (g <= 0xFFFF) + g = 0; + + b = 76310 * y2 + 132278 * u; + if (b > 0xFFFFFF) + b = 0xFFFFFF; + if (b <= 0xFFFF) + b = 0; + + buf->r = (UCHAR)(r >> 16); + buf->g = (UCHAR)(g >> 16); + buf->b = (UCHAR)(b >> 16); + buf->m = (UCHAR)255; + buf++; + } +} + +//****************************************************************** +// Main conversion function +//****************************************************************** + +void TRop::convert(TRasterP dst, const TRasterP &src) +{ + if (dst->getSize() != src->getSize()) + throw TRopException("convert: size mismatch"); + + TRaster32P dst32 = dst; + TRasterGR8P dst8 = dst; + TRasterGR16P dst16 = dst; + TRaster64P dst64 = dst; + TRasterCM32P dstCm = dst; + + TRaster32P src32 = src; + TRasterGR8P src8 = src; + TRaster64P src64 = src; + TRasterYUV422P srcYUV = src; + TRasterYUV422P dstYUV = dst; + + src->lock(); + dst->lock(); + + if (dst64 && src32) + do_convert(dst64, src32); + else if (dst8 && src32) + do_convert(dst8, src32); + else if (dst16 && src32) + do_convert(dst16, src32); + else if (dst32 && src64) + ditherConvert(src64, dst32); + else if (dst16 && src64) + do_convert(dst16, src64); + else if (dst32 && src8) + do_convert(dst32, src8); + else if (dstYUV && src32) + do_convert(dstYUV, src32); // Obsolete conversions + else if (dst32 && srcYUV) + do_convert(dst32, srcYUV); // + else if (dstCm && src32) + do_convert(dstCm, src32); // + else if (dstCm && src8) + do_convert(dstCm, src8); // + else { + dst->unlock(); + src->unlock(); + + throw TRopException("unsupported pixel type"); + } + + dst->unlock(); + src->unlock(); +} diff --git a/toonz/sources/common/trop/tconvolve.cpp b/toonz/sources/common/trop/tconvolve.cpp new file mode 100644 index 0000000..909e2d3 --- /dev/null +++ b/toonz/sources/common/trop/tconvolve.cpp @@ -0,0 +1,662 @@ + + +#include "traster.h" +#include "trastercm.h" +#include "tpalette.h" +#include "tcolorstyles.h" +//#include "trop.h" +#include "tropcm.h" +#include "tpixelutils.h" + +#define TMIN(a, b) (a < b ? a : b) +#define TMAX(a, b) (a > b ? a : b) + +//------------------------------------------------------------------------------ + +namespace +{ + +//------------------------------------------------------------------------------ + +template +void doConvolve_row_9_i(PIXOUT *pixout, int n, PIXIN *pixarr[], long w[]) +{ + long w1, w2, w3, w4, w5, w6, w7, w8, w9; + PIXIN *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9; + w1 = w[0]; + w2 = w[1]; + w3 = w[2]; + w4 = w[3]; + w5 = w[4]; + w6 = w[5]; + w7 = w[6]; + w8 = w[7]; + w9 = w[8]; + p1 = pixarr[0]; + p2 = pixarr[1]; + p3 = pixarr[2]; + p4 = pixarr[3]; + p5 = pixarr[4]; + p6 = pixarr[5]; + p7 = pixarr[6]; + p8 = pixarr[7]; + p9 = pixarr[8]; + + int rightShift = 16 + ((int)sizeof(typename PIXIN::Channel) - (int)sizeof(typename PIXOUT::Channel)) * 8; + + while (n-- > 0) { + pixout->r = (typename PIXOUT::Channel)((p1->r * w1 + p2->r * w2 + p3->r * w3 + + p4->r * w4 + p5->r * w5 + p6->r * w6 + + p7->r * w7 + p8->r * w8 + p9->r * w9 + (1 << 15)) >> + rightShift); + pixout->g = (typename PIXOUT::Channel)((p1->g * w1 + p2->g * w2 + p3->g * w3 + + p4->g * w4 + p5->g * w5 + p6->g * w6 + + p7->g * w7 + p8->g * w8 + p9->g * w9 + (1 << 15)) >> + rightShift); + pixout->b = (typename PIXOUT::Channel)((p1->b * w1 + p2->b * w2 + p3->b * w3 + + p4->b * w4 + p5->b * w5 + p6->b * w6 + + p7->b * w7 + p8->b * w8 + p9->b * w9 + (1 << 15)) >> + rightShift); + pixout->m = (typename PIXOUT::Channel)((p1->m * w1 + p2->m * w2 + p3->m * w3 + + p4->m * w4 + p5->m * w5 + p6->m * w6 + + p7->m * w7 + p8->m * w8 + p9->m * w9 + (1 << 15)) >> + rightShift); + + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + p7++; + p8++; + p9++; + pixout++; + } +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_cm32_row_9_i(PIXOUT *pixout, int n, + TPixelCM32 *pixarr[], long w[], + const vector &paints, + const vector &inks) +{ + long w1, w2, w3, w4, w5, w6, w7, w8, w9; + TPixelCM32 *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9; + TPixel32 val[9]; + + w1 = w[0]; + w2 = w[1]; + w3 = w[2]; + w4 = w[3]; + w5 = w[4]; + w6 = w[5]; + w7 = w[6]; + w8 = w[7]; + w9 = w[8]; + p1 = pixarr[0]; + p2 = pixarr[1]; + p3 = pixarr[2]; + p4 = pixarr[3]; + p5 = pixarr[4]; + p6 = pixarr[5]; + p7 = pixarr[6]; + p8 = pixarr[7]; + p9 = pixarr[8]; + while (n-- > 0) { + for (int i = 0; i < 9; ++i) { + int tone = p1->getTone(); + int paint = p1->getPaint(); + int ink = p1->getInk(); + if (tone == TPixelCM32::getMaxTone()) + val[i] = paints[paint]; + else if (tone == 0) + val[i] = inks[ink]; + else + val[i] = blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone()); + } + + pixout->r = (typename PIXOUT::Channel)((val[1].r * w1 + val[2].r * w2 + val[3].r * w3 + + val[4].r * w4 + val[5].r * w5 + val[6].r * w6 + + val[7].r * w7 + val[8].r * w8 + val[9].r * w9 + (1 << 15)) >> + 16); + pixout->g = (typename PIXOUT::Channel)((val[1].g * w1 + val[2].g * w2 + val[3].g * w3 + + val[4].g * w4 + val[5].g * w5 + val[6].g * w6 + + val[7].g * w7 + val[8].g * w8 + val[9].g * w9 + (1 << 15)) >> + 16); + pixout->b = (typename PIXOUT::Channel)((val[1].b * w1 + val[2].b * w2 + val[3].b * w3 + + val[4].b * w4 + val[5].b * w5 + val[6].b * w6 + + val[7].b * w7 + val[8].b * w8 + val[9].b * w9 + (1 << 15)) >> + 16); + pixout->m = (typename PIXOUT::Channel)((val[1].m * w1 + val[2].m * w2 + val[3].m * w3 + + val[4].m * w4 + val[5].m * w5 + val[6].m * w6 + + val[7].m * w7 + val[8].m * w8 + val[9].m * w9 + (1 << 15)) >> + 16); + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + p7++; + p8++; + p9++; + pixout++; + } +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_row_i(PIXOUT *pixout, int n, + PIXIN *pixarr[], long w[], int pixn) +{ + long ar, ag, ab, am; + int i; + + int rightShift = 16 + ((int)sizeof(typename PIXIN::Channel) - (int)sizeof(typename PIXOUT::Channel)) * 8; + + while (n-- > 0) { + ar = ag = ab = am = 0; + for (i = 0; i < pixn; i++) { + ar += pixarr[i]->r * w[i]; + ag += pixarr[i]->g * w[i]; + ab += pixarr[i]->b * w[i]; + am += pixarr[i]->m * w[i]; + pixarr[i]++; + } + pixout->r = (typename PIXOUT::Channel)((ar + (1 << 15)) >> rightShift); + pixout->g = (typename PIXOUT::Channel)((ag + (1 << 15)) >> rightShift); + pixout->b = (typename PIXOUT::Channel)((ab + (1 << 15)) >> rightShift); + pixout->m = (typename PIXOUT::Channel)((am + (1 << 15)) >> rightShift); + + pixout++; + } +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_cm32_row_i(PIXOUT *pixout, int n, + TPixelCM32 *pixarr[], long w[], int pixn, + const vector &paints, + const vector &inks) +{ + long ar, ag, ab, am; + int i; + + while (n-- > 0) { + ar = ag = ab = am = 0; + for (i = 0; i < pixn; i++) { + TPixel32 val; + int tone = pixarr[i]->getTone(); + int paint = pixarr[i]->getPaint(); + int ink = pixarr[i]->getInk(); + if (tone == TPixelCM32::getMaxTone()) + val = paints[paint]; + else if (tone == 0) + val = inks[ink]; + else + val = blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone()); + + ar += val.r * w[i]; + ag += val.g * w[i]; + ab += val.b * w[i]; + am += val.m * w[i]; + pixarr[i]++; + } + pixout->r = (typename PIXOUT::Channel)((ar + (1 << 15)) >> 16); + pixout->g = (typename PIXOUT::Channel)((ag + (1 << 15)) >> 16); + pixout->b = (typename PIXOUT::Channel)((ab + (1 << 15)) >> 16); + pixout->m = (typename PIXOUT::Channel)((am + (1 << 15)) >> 16); + pixout++; + } +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_3_i(TRasterPT rout, + TRasterPT rin, + int dx, int dy, + double conv[]) +{ + PIXIN *bufferin; + PIXOUT *bufferout; + PIXIN *pixin; + PIXOUT *pixout; + + PIXIN *pixarr[9]; + long w[9]; + int pixn; + int wrapin, wrapout; + int x, y, n; + int x1, y1, x2, y2; + int fx1, fy1, fx2, fy2, fx, fy; + + rout->clear(); + + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + /* calcolo l'area di output interessata */ + x1 = TMAX(0, -dx - 1); + y1 = TMAX(0, -dy - 1); + x2 = TMIN(rout->getLx() - 1, -dx + rin->getLx()); + y2 = TMIN(rout->getLy() - 1, -dy + rin->getLy()); + + rin->lock(); + rout->lock(); + bufferin = rin->pixels(); + bufferout = rout->pixels(); + + for (y = y1; y <= y2; y++) { + fy1 = TMAX(-1, -dy - y); + fy2 = TMIN(1, -dy + rin->getLy() - 1 - y); + if (fy1 > fy2) + continue; + x = x1; + pixout = bufferout + wrapout * y + x; + pixin = bufferin + wrapin * (y + dy) + (x + dx); + + while (x <= x2) { + fx1 = TMAX(-1, -dx - x); + fx2 = TMIN(1, -dx + rin->getLx() - 1 - x); + if (x > -dx && x < -dx + rin->getLx() - 1) + n = tmin(-dx + rin->getLx() - 1 - x, x2 - x + 1); + else + n = 1; + if (n < 1) + break; + pixn = 0; + for (fy = fy1; fy <= fy2; fy++) + for (fx = fx1; fx <= fx2; fx++) { + pixarr[pixn] = pixin + fy * wrapin + fx; + w[pixn] = (long)(conv[(fy + 1) * 3 + fx + 1] * (1 << 16)); + pixn++; + } + if (pixn == 9) + doConvolve_row_9_i(pixout, n, pixarr, w); + else + doConvolve_row_i(pixout, n, pixarr, w, pixn); + x += n; + pixin += n; + pixout += n; + } + } + rin->unlock(); + rout->unlock(); +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_i(TRasterPT rout, + TRasterPT rin, + int dx, int dy, + double conv[], int radius) +{ + PIXIN *bufferin; + PIXOUT *bufferout; + PIXIN *pixin; + PIXOUT *pixout; + + int radiusSquare = sq(radius); + PIXIN **pixarr = new PIXIN *[radiusSquare]; + long *w = new long[radiusSquare]; + int pixn; + int wrapin, wrapout; + int x, y, n; + int x1, y1, x2, y2; + int fx1, fy1, fx2, fy2, fx, fy; + int radius1 = radius / 2; + int radius0 = radius1 - radius + 1; + + rout->clear(); + + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + /* calcolo l'area di output interessata */ + x1 = TMAX(0, -dx - 1); + y1 = TMAX(0, -dy - 1); + x2 = TMIN(rout->getLx() - 1, -dx + rin->getLx()); + y2 = TMIN(rout->getLy() - 1, -dy + rin->getLy()); + + rin->lock(); + rout->lock(); + bufferin = rin->pixels(); + bufferout = rout->pixels(); + + for (y = y1; y <= y2; y++) { + fy1 = TMAX(radius0, -dy - y); + fy2 = TMIN(radius1, -dy - y + rin->getLy() - 1); + if (fy1 > fy2) + continue; + x = x1; + pixout = bufferout + wrapout * y + x; + pixin = bufferin + wrapin * (y + dy) + (x + dx); + + while (x <= x2) { + fx1 = TMAX(radius0, -dx - x); + fx2 = TMIN(radius1, -dx - x + rin->getLx() - 1); + if (x > -dx && x < -dx + rin->getLx() - 1) + n = tmin(-dx + rin->getLx() - 1 - x, x2 - x + 1); + else + n = 1; + if (n < 1) + break; + pixn = 0; + for (fy = fy1; fy <= fy2; fy++) + for (fx = fx1; fx <= fx2; fx++) { + pixarr[pixn] = pixin + fy * wrapin + fx; + w[pixn] = (long)(conv[(fy - radius0) * radius + fx - radius0] * (1 << 16)); + pixn++; + } + + doConvolve_row_i(pixout, n, pixarr, w, pixn); + + x += n; + pixin += n; + pixout += n; + } + } + + rin->unlock(); + rout->unlock(); + + delete[] w; + delete[] pixarr; +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_cm32_3_i(TRasterPT rout, + TRasterCM32P rin, + const TPaletteP &palette, + int dx, int dy, + double conv[]) +{ + TPixelCM32 *pixin; + PIXOUT *pixout; + TPixelCM32 *pixarr[9]; + long w[9]; + int pixn; + int wrapin, wrapout; + int x, y, n; + int x1, y1, x2, y2; + int fx1, fy1, fx2, fy2, fx, fy; + + rout->clear(); + + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + /* calcolo l'area di output interessata */ + x1 = TMAX(0, -dx - 1); + y1 = TMAX(0, -dy - 1); + x2 = TMIN(rout->getLx() - 1, -dx + rin->getLx()); + y2 = TMIN(rout->getLy() - 1, -dy + rin->getLy()); + + int colorCount = palette->getStyleCount(); + colorCount = tmax(colorCount, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + std::vector paints(colorCount); + std::vector inks(colorCount); + + rin->lock(); + rout->lock(); + TPixelCM32 *bufferin = rin->pixels(); + PIXOUT *bufferout = rout->pixels(); + + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = palette->getStyle(i)->getAverageColor(); + + for (y = y1; y <= y2; y++) { + fy1 = TMAX(-1, -dy - y); + fy2 = TMIN(1, -dy + rin->getLy() - 1 - y); + if (fy1 > fy2) + continue; + x = x1; + pixout = bufferout + wrapout * y + x; + pixin = bufferin + wrapin * (y + dy) + (x + dx); + + while (x <= x2) { + fx1 = TMAX(-1, -dx - x); + fx2 = TMIN(1, -dx + rin->getLx() - 1 - x); + if (x > -dx && x < -dx + rin->getLx() - 1) + n = TMIN(-dx + rin->getLx() - 1 - x, x2 - x + 1); + else + n = 1; + if (n < 1) + break; + pixn = 0; + for (fy = fy1; fy <= fy2; fy++) + for (fx = fx1; fx <= fx2; fx++) { + pixarr[pixn] = pixin + fy * wrapin + fx; + w[pixn] = (long)(conv[(fy + 1) * 3 + fx + 1] * (1 << 16)); + pixn++; + } + if (pixn == 9) + doConvolve_cm32_row_9_i(pixout, n, pixarr, w, paints, inks); + else + doConvolve_cm32_row_i(pixout, n, pixarr, w, pixn, paints, inks); + x += n; + pixin += n; + pixout += n; + } + } + rin->unlock(); + rout->unlock(); +} + +//------------------------------------------------------------------------------ + +template +void doConvolve_cm32_i(TRasterPT rout, + TRasterCM32P rin, + const TPaletteP &palette, + int dx, int dy, + double conv[], int radius) +{ + TPixelCM32 *pixin; + PIXOUT *pixout; + int radiusSquare = sq(radius); + TPixelCM32 **pixarr = new TPixelCM32 *[radiusSquare]; + long *w = new long[radiusSquare]; + int pixn; + int wrapin, wrapout; + int x, y, n; + int x1, y1, x2, y2; + int fx1, fy1, fx2, fy2, fx, fy; + int radius1 = radius / 2; + int radius0 = radius1 - radius + 1; + + rout->clear(); + + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + /* calcolo l'area di output interessata */ + x1 = TMAX(0, -dx - 1); + y1 = TMAX(0, -dy - 1); + x2 = TMIN(rout->getLx() - 1, -dx + rin->getLx()); + y2 = TMIN(rout->getLy() - 1, -dy + rin->getLy()); + + int colorCount = palette->getStyleCount(); + colorCount = tmax(colorCount, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + std::vector paints(colorCount); + std::vector inks(colorCount); + + rin->lock(); + rout->lock(); + TPixelCM32 *bufferin = rin->pixels(); + PIXOUT *bufferout = rout->pixels(); + + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = palette->getStyle(i)->getAverageColor(); + + for (y = y1; y <= y2; y++) { + fy1 = TMAX(radius0, -dy - y); + fy2 = TMIN(radius1, -dy + rin->getLy() - 1 - y); + if (fy1 > fy2) + continue; + x = x1; + pixout = bufferout + wrapout * y + x; + pixin = bufferin + wrapin * (y + dy) + (x + dx); + + while (x <= x2) { + fx1 = TMAX(radius0, -dx - x); + fx2 = TMIN(radius1, -dx + rin->getLx() - 1 - x); + if (x > -dx && x < -dx + rin->getLx() - 1) + n = TMIN(-dx + rin->getLx() - 1 - x, x2 - x + 1); + else + n = 1; + if (n < 1) + break; + pixn = 0; + for (fy = fy1; fy <= fy2; fy++) + for (fx = fx1; fx <= fx2; fx++) { + pixarr[pixn] = pixin + fy * wrapin + fx; + w[pixn] = (long)(conv[(fy - radius0) * radius + fx - radius0] * (1 << 16)); + pixn++; + } + + doConvolve_cm32_row_i(pixout, n, pixarr, w, pixn, paints, inks); + + x += n; + pixin += n; + pixout += n; + } + } + + rin->unlock(); + rout->unlock(); + + delete[] pixarr; + delete[] w; +} + +} // anonymous namespace + +//------------------------------------------------------------------------------ + +void TRop::convolve_3_i(TRasterP rout, TRasterP rin, int dx, int dy, double conv[]) +{ + TRaster32P rin32 = rin; + + if (rin32) { + TRaster32P rout32 = rout; + if (rout32) { + doConvolve_3_i(rout32, rin32, dx, dy, conv); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_3_i(rout64, rin32, dx, dy, conv); + return; + } + } else { + TRaster64P rin64 = rin; + if (rin64) { + TRaster32P rout32 = rout; + if (rout32) { + doConvolve_3_i(rout32, rin64, dx, dy, conv); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_3_i(rout64, rin64, dx, dy, conv); + return; + } + } + } + + throw TRopException("TRop::convolve_3_i: unsupported pixel type"); +} + +//------------------------------------------------------------------------------ + +void TRop::convolve_3_i(TRasterP rout, TRasterCM32P rin, const TPaletteP &palette, + int dx, int dy, double conv[]) +{ + TRaster32P rout32 = rout; + + if (rout32) { + doConvolve_cm32_3_i(rout32, rin, palette, dx, dy, conv); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_cm32_3_i(rout64, rin, palette, dx, dy, conv); + return; + } + + throw TRopException("TRop::convolve_3_i: unsupported pixel type"); +} + +//------------------------------------------------------------------------------ + +void TRop::convolve_i(TRasterP rout, TRasterP rin, int dx, int dy, double conv[], int radius) +{ + TRaster32P rin32 = rin; + + if (rin32) { + TRaster32P rout32 = rout; + if (rout32) { + doConvolve_i(rout32, rin32, dx, dy, conv, radius); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_i(rout64, rin32, dx, dy, conv, radius); + return; + } + } else { + TRaster64P rin64 = rin; + if (rin64) { + TRaster32P rout32 = rout; + if (rout32) { + doConvolve_i(rout32, rin64, dx, dy, conv, radius); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_i(rout64, rin64, dx, dy, conv, radius); + return; + } + } + } + + throw TRopException("TRop::convolve_i: unsupported pixel type"); +} + +//------------------------------------------------------------------------------ + +void TRop::convolve_i(TRasterP rout, TRasterCM32P rin, const TPaletteP &palette, + int dx, int dy, double conv[], int radius) +{ + TRaster32P rout32 = rout; + + if (rout32) { + doConvolve_cm32_i(rout32, rin, palette, dx, dy, conv, radius); + return; + } + + TRaster64P rout64 = rout; + if (rout64) { + doConvolve_cm32_i(rout64, rin, palette, dx, dy, conv, radius); + return; + } + + throw TRopException("TRop::convolve_i: unsupported pixel type"); +} diff --git a/toonz/sources/common/trop/tdespeckle.cpp b/toonz/sources/common/trop/tdespeckle.cpp new file mode 100644 index 0000000..6349665 --- /dev/null +++ b/toonz/sources/common/trop/tdespeckle.cpp @@ -0,0 +1,723 @@ + + +#include "tropcm.h" + +// TnzCore includes +#include "tpalette.h" +#include "trop_borders.h" + +#include "pixelselectors.h" +#include "runsmap.h" + +#define INCLUDE_HPP +#include "raster_edge_iterator.h" +#include "borders_extractor.h" +#define INCLUDE_HPP + +// tcg includes +#include "tcg/deleter_types.h" + +// STL includes +#include + +/*============================================================================================ + + Explanation + + Despeckling is a noise-removal procedure which aims at eliminating small blots of color + from an image. + + We will assume that speckles are recognized with uniform color (which means - image + discretization should be externally performed). + + We will currently assume that the despeckling procedure works only on the ink or paint plane of a + TRasterCM32 instance, not both (should be extended in the future). + + The image is traversed to isolate regions with the same color and, if their area is + below the specified threshold, they get removed or changed of color. + + Color change looks at the area's neighbouring pixels for the most used color to use for + replacement. If NO neighbouring color can be found, the area is made transparent. + +==============================================================================================*/ + +using namespace TRop::borders; + +namespace +{ + +//************************************************************************ +// Pixel Selectors +//************************************************************************ + +class InkSelectorCM32 +{ +public: + typedef TPixelCM32 pixel_type; + typedef TUINT32 value_type; + +public: + InkSelectorCM32() {} + + value_type transparent() const { return 0; } + bool transparent(const pixel_type &pix) const { return value(pix) == 0; } + + value_type value(const pixel_type &pix) const { return pix.getInk(); } + bool equal(const pixel_type &a, const pixel_type &b) const { return value(a) == value(b); } + + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return true; } +}; + +//------------------------------------------------------------------------------------------ + +template +class InkSelectorRGBM +{ + bool m_transparentIsWhite; + +public: + typedef PIXEL pixel_type; + typedef CHANNEL value_type; + +public: + InkSelectorRGBM(bool transparentIsWhite) : m_transparentIsWhite(transparentIsWhite) {} + + value_type transparent() const { return 0; } + bool transparent(const pixel_type &pix) const { return value(pix) == 0; } + + value_type value(const pixel_type &pix) const + { + if (m_transparentIsWhite) + return (pix == PIXEL::White) ? 0 : 1; + else + return (pix.m == 0) ? 0 : 1; + } + bool equal(const pixel_type &a, const pixel_type &b) const { return value(a) == value(b); } + + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return true; } +}; + +//------------------------------------------------------------------------------------------ + +template +class InkSelectorGR +{ +public: + typedef PIXEL pixel_type; + typedef CHANNEL value_type; + +public: + InkSelectorGR() {} + + value_type transparent() const { return PIXEL::maxChannelValue; } + bool transparent(const pixel_type &pix) const { return value(pix) == 0; } + + value_type value(const pixel_type &pix) const { return (pix.value == PIXEL::maxChannelValue) ? 0 : 1; } + bool equal(const pixel_type &a, const pixel_type &b) const { return value(a) == value(b); } + + bool skip(const value_type &prevLeftValue, const value_type &leftValue) const { return true; } +}; + +//************************************************************************ +// Border class +//************************************************************************ + +struct Border { + std::vector m_points; + int m_x0, m_y0, m_x1, m_y1; + + Border() : m_x0((std::numeric_limits::max)()), m_y0(m_x0), m_x1(-m_x0), m_y1(m_x1) {} + + void addPoint(const TPoint &p) + { + //Update the region box + if (p.x < m_x0) + m_x0 = p.x; + if (p.x > m_x1) + m_x1 = p.x; + if (p.y < m_y0) + m_y0 = p.y; + if (p.y > m_y1) + m_y1 = p.y; + + //Add the vertex + m_points.push_back(p); + } + + void clear() + { + m_points.clear(); + m_x0 = (std::numeric_limits::max)(), m_y0 = m_x0, m_x1 = -m_x0, m_y1 = m_x1; + } +}; + +//************************************************************************ +// Base classes +//************************************************************************ + +//======================================================================== +// Borders Painter +//======================================================================== + +template +class BordersPainter +{ +public: + typedef Pix pixel_type; + +protected: + TRasterPT m_ras; + RunsMapP m_runsMap; + +public: + BordersPainter(const TRasterPT &ras) : m_ras(ras) {} + + const TRasterPT &ras() { return m_ras; } + RunsMapP &runsMap() { return m_runsMap; } + + void paintLine(int x, int y0, int y1) const; + void paintBorder(const Border &border) const; + void paintBorders(const std::deque &borders) const + { + size_t i, size = borders.size(); + for (i = 0; i < size; ++i) + paintBorder(*borders[i]); + } + +protected: + virtual void paintPixel(pixel_type *pix) const = 0; +}; + +//--------------------------------------------------------------------------------------------- + +template +void BordersPainter::paintBorder(const Border &border) const +{ + const std::vector &points = border.m_points; + + size_t i, size_1 = points.size() - 1; + for (i = 0; i < size_1; ++i) { + const TPoint &p = points[i]; + paintLine(p.x, p.y, points[i + 1].y); + } + paintLine(points[size_1].x, points[size_1].y, points[0].y); +} + +//--------------------------------------------------------------------------------------------- + +template +void BordersPainter::paintLine(int x, int y0, int y1) const +{ + for (int j = y0; j < y1; ++j) { + TPixelGR8 *runPix = m_runsMap->pixels(j) + x; + int l, runLength = 0, hierarchy = 0; + + do { + if (runPix->value & TRop::borders::_HIERARCHY_INCREASE) + ++hierarchy; + + //Update vars + runLength += l = m_runsMap->runLength(runPix); + runPix += l; + + if ((runPix - 1)->value & TRop::borders::_HIERARCHY_DECREASE) + --hierarchy; + } while (hierarchy > 0); + + pixel_type *pix = m_ras->pixels(j) + x, *pixEnd = pix + runLength; + + for (; pix < pixEnd; ++pix) + paintPixel(pix); + } +} + +//======================================================================== +// Despeckling Reader +//======================================================================== + +class DespecklingReader +{ +protected: + std::deque m_borders; + Border m_border; + + int m_sizeTol; + +public: + DespecklingReader(int sizeTol) : m_sizeTol(sizeTol) {} + ~DespecklingReader(); + + int sizeTol() const { return m_sizeTol; } + bool isSpeckle(const Border &border) + { + return border.m_x1 - border.m_x0 <= m_sizeTol && + border.m_y1 - border.m_y0 <= m_sizeTol; + } + + void openContainer(const TPoint &pos); + void addElement(const TPoint &pos); + virtual void closeContainer(); + + const std::deque &borders() const { return m_borders; } + std::deque &borders() { return m_borders; } +}; + +//--------------------------------------------------------------------------------------------- + +DespecklingReader::~DespecklingReader() +{ + std::for_each(m_borders.begin(), m_borders.end(), tcg::deleter()); +} + +//--------------------------------------------------------------------------------------------- + +void DespecklingReader::openContainer(const TPoint &pos) +{ + m_border.clear(); + m_border.addPoint(pos); +} + +//--------------------------------------------------------------------------------------------- + +void DespecklingReader::addElement(const TPoint &pos) +{ + m_border.addPoint(pos); +} + +//--------------------------------------------------------------------------------------------- + +void DespecklingReader::closeContainer() +{ + if (isSpeckle(m_border)) + m_borders.push_back(new Border(m_border)); +} + +//************************************************************************ +// Specialized classes +//************************************************************************ + +//======================================================================== +// Replace Painters +//======================================================================== + +template +class ReplacePainter : public BordersPainter +{ + typename ReplacePainter::pixel_type m_color; + +public: + ReplacePainter(const TRasterPT &ras) + : BordersPainter(ras) {} + ReplacePainter(const TRasterPT &ras, const typename ReplacePainter::pixel_type &color) + : BordersPainter(ras), m_color(color) {} + + const typename ReplacePainter::pixel_type &color() const { return m_color; } + typename ReplacePainter::pixel_type &color() { return m_color; } + + void paintPixel(typename ReplacePainter::pixel_type *pix) const { *pix = m_color; } +}; + +//--------------------------------------------------------------------------------------------- + +template <> +class ReplacePainter : public BordersPainter +{ + TUINT32 m_value; + TUINT32 m_keepMask; + +public: + ReplacePainter(const TRasterPT &ras) + : BordersPainter(ras), m_value(0), m_keepMask(0) {} + ReplacePainter(const TRasterPT &ras, TUINT32 value, TUINT32 keepMask) + : BordersPainter(ras), m_value(value), m_keepMask(keepMask) {} + + const TUINT32 &value() const { return m_value; } + TUINT32 &value() { return m_value; } + + const TUINT32 &keepMask() const { return m_keepMask; } + TUINT32 &keepMask() { return m_keepMask; } + + void paintPixel(pixel_type *pix) const { *pix = TPixelCM32(m_value | (pix->getValue() & m_keepMask)); } +}; + +//======================================================================== +// Isolated Despeckling +//======================================================================== + +template +class IsolatedReader : public DespecklingReader +{ +public: + typedef typename PixelSelector::pixel_type pixel_type; + typedef typename PixelSelector::value_type value_type; + +private: + const PixelSelector &m_selector; + bool m_ok; + +public: + IsolatedReader(const PixelSelector &selector, int sizeTol); + + void openContainer(const RasterEdgeIterator &it); + void addElement(const RasterEdgeIterator &it); + void closeContainer(); +}; + +//--------------------------------------------------------------------------------------------- + +template +IsolatedReader::IsolatedReader(const PixelSelector &selector, int sizeTol) + : DespecklingReader(sizeTol), m_selector(selector) +{ +} + +//--------------------------------------------------------------------------------------------- + +template +void IsolatedReader::openContainer(const RasterEdgeIterator &it) +{ + m_ok = (it.leftColor() == m_selector.transparent()); + if (!m_ok) + return; + + DespecklingReader::openContainer(it.pos()); +} + +//--------------------------------------------------------------------------------------------- + +template +void IsolatedReader::addElement(const RasterEdgeIterator &it) +{ + if (!m_ok) + return; + m_ok = (it.leftColor() == m_selector.transparent()); + if (!m_ok) + return; + + DespecklingReader::addElement(it.pos()); +} + +//--------------------------------------------------------------------------------------------- + +template +void IsolatedReader::closeContainer() +{ + if (m_ok) + DespecklingReader::closeContainer(); +} + +} //namespace + +//********************************************************************************************************* +// Despeckling Mains +//********************************************************************************************************* + +/*! + Applies despeckling (paint or removal of small blots of uniform color) to the image. +*/ + +template +void doDespeckleRGBM(const TRasterPT &ras, int sizeThreshold, bool transparentIsWhite) +{ + InkSelectorRGBM selector(transparentIsWhite); + IsolatedReader> reader(selector, sizeThreshold); + ReplacePainter painter(ras, transparentIsWhite ? PIXEL::White : PIXEL::Transparent); + + TRop::borders::readBorders(ras, selector, reader, &painter.runsMap()); + painter.paintBorders(reader.borders()); +} + +//---------------------------------------------------- + +template +void doDespeckleGR(const TRasterPT &ras, int sizeThreshold) +{ + InkSelectorGR selector; + IsolatedReader> reader(selector, sizeThreshold); + ReplacePainter painter(ras, PIXEL::maxChannelValue); + + TRop::borders::readBorders(ras, selector, reader, &painter.runsMap()); + painter.paintBorders(reader.borders()); +} + +//---------------------------------------------------- + +void doDespeckleCM32(const TRasterPT &ras, int sizeThreshold, bool check) +{ + TRasterCM32P rasCM(ras); + rasCM->lock(); + + InkSelectorCM32 selector; + IsolatedReader reader(selector, sizeThreshold); + ReplacePainter painter(rasCM, check ? 0xffffff00 : 0x000000ff, 0); // 0xffffff00 is a special non-mapped full ink pixel + // 0x000000ff is a full transparent paint pixel + TRop::borders::readBorders(rasCM, selector, reader, &painter.runsMap()); + painter.paintBorders(reader.borders()); + + rasCM->unlock(); +} + +//--------------------------------------------------------------------------------------------- + +void TRop::despeckle(const TRasterP &ras, int sizeThreshold, bool check, bool transparentIsWhite) +{ + ras->lock(); + + if (TRasterCM32P(ras)) { + doDespeckleCM32(ras, sizeThreshold, check); + return; + } + if (TRaster32P(ras)) { + doDespeckleRGBM(ras, sizeThreshold, transparentIsWhite); + return; + } + if (TRaster64P(ras)) { + doDespeckleRGBM(ras, sizeThreshold, transparentIsWhite); + return; + } + if (TRasterGR8P(ras)) { + doDespeckleGR(ras, sizeThreshold); + return; + } + if (TRasterGR16P(ras)) { + doDespeckleGR(ras, sizeThreshold); + return; + } + + ras->unlock(); +} + +//--------------------------------------------------------------------------------------------- + +/*! + Performs a copy of rin and then applies despeckling. +*/ +void TRop::despeckle(const TRasterP &rout, const TRasterP &rin, int sizeThreshold, bool check) +{ + TRop::copy(rout, rin); + TRop::despeckle(rout, sizeThreshold, check); +} + +//********************************************************************************************************* +// Majority Despeckling +//********************************************************************************************************* + +namespace +{ + +template +class FillingReader : public DespecklingReader +{ +public: + typedef typename PixelSelector::pixel_type pixel_type; + typedef typename PixelSelector::value_type value_type; + +private: + ReplacePainter m_painter; + +public: + FillingReader(const TRasterGR8P &rasGR, int sizeTol) + : DespecklingReader(sizeTol), m_painter(rasGR, TPixelGR8::Black) {} + + void openContainer(const RasterEdgeIterator &it); + void addElement(const RasterEdgeIterator &it); + void closeContainer(); + + RunsMapP &runsMap() { return m_painter.runsMap(); } +}; + +//--------------------------------------------------------------------------------------------- + +template +void FillingReader::openContainer(const RasterEdgeIterator &it) +{ + DespecklingReader::openContainer(it.pos()); +} + +//--------------------------------------------------------------------------------------------- + +template +void FillingReader::addElement(const RasterEdgeIterator &it) +{ + DespecklingReader::addElement(it.pos()); +} + +//--------------------------------------------------------------------------------------------- + +template +void FillingReader::closeContainer() +{ + if (isSpeckle(m_border)) + m_painter.paintBorder(m_border); + + DespecklingReader::closeContainer(); +} + +//============================================================================================= + +inline TPoint direction(const TPoint &a, const TPoint &b) +{ + return TPoint((b.x > a.x) ? 1 : (b.x < a.x) ? -1 : 0, + (b.y > a.y) ? 1 : (b.y < a.y) ? -1 : 0); +} + +//--------------------------------------------------------------------------------------------- + +template +bool majority(const TRasterPT ras, const TRasterGR8P &rasGR, + const PixelSelector &selector, const Border &border, + typename PixelSelector::value_type &color) +{ + typedef typename PixelSelector::value_type value_type; + + //Build a histogram of all found colors around the border + std::map histogram; + + Pixel *pix, *basePix = ras->pixels(0); + TPixelGR8 *grPix; + + int diff, x, y, lx = ras->getLx(), ly = ras->getLy(), wrap = ras->getWrap(); + + assert(border.m_points[1].y > border.m_points[0].y); + + //Iterate the raster along the border + const std::vector &points = border.m_points; + + RasterEdgeIterator + start(ras, selector, points[0], direction(points[0], points[1])), + it(start); + + size_t next = 1, size = points.size(); + do { + while (it.pos() != points[next]) { + pix = it.leftPix(); + diff = pix - basePix; + x = diff % wrap; + y = diff / wrap; + + if (x >= 0 && y >= 0 && x < lx && y < ly) { + grPix = rasGR->pixels(y) + x; + if (grPix->value) + ++histogram[it.leftColor()]; + } + + it.setEdge(it.pos() + it.dir(), it.dir()); + } + + next = (next + 1) % size; + it.setEdge(it.pos(), direction(it.pos(), points[next])); + } while (it != start); + + if (!histogram.empty()) { + //Return the most found color + color = histogram.begin()->first; + return true; + } + + return false; +} + +//--------------------------------------------------------------------------------------------- + +template +void majorityDespeckle(const TRasterPT &ras, int sizeThreshold) +{ + typedef typename TRop::borders::PixelSelector pixel_selector; + typedef typename pixel_selector::pixel_type pixel_type; + typedef typename pixel_selector::value_type value_type; + + ras->lock(); + + //Use a temporary bitmap (well, a bytemap - for now?) to store the found speckles + TRasterGR8P rasGR(ras->getSize()); + rasGR->fill(TPixelGR8::White); + + //Find the speckles and draw them on the bitmap + pixel_selector selector; + FillingReader reader(rasGR, sizeThreshold); + + TRop::borders::readBorders(ras, selector, reader, &reader.runsMap()); + + //Now, operate each speckle. Try to apply a majority color to the speckle + ReplacePainter painter(ras); + painter.runsMap() = reader.runsMap(); + + ReplacePainter painterGR(rasGR, TPixelGR8::White); + painterGR.runsMap() = reader.runsMap(); + + std::deque borders = reader.borders(); //Note that the DEEP copy is NEEDED + + int processedCount = 1; + while (processedCount > 0 && !borders.empty()) { + processedCount = 0; + + //Traverse the speckles list. Try to apply majority. + Border *current, *last = borders.back(); + + do { + current = borders.front(); + borders.pop_front(); + + value_type color; + if (majority(ras, rasGR, selector, *current, color)) { + ++processedCount; + painter.color() = color; + + painter.paintBorder(*current); + painterGR.paintBorder(*current); + } else + borders.push_back(current); + + } while (current != last); + } + + //Speckles may remain. In this case, fill with transparent. + painter.color() = selector.transparent(); + while (!borders.empty()) { + Border *current = borders.front(); + painter.paintBorder(*current); + borders.pop_front(); + } + + ras->unlock(); +} + +} //namespace + +//================================================================================================ + +void TRop::majorityDespeckle(const TRasterP &ras, int sizeThreshold) +{ + TRaster32P ras32(ras); + if (ras32) { + ::majorityDespeckle(ras32, sizeThreshold); + return; + } + + TRaster64P ras64(ras); + if (ras64) { + ::majorityDespeckle(ras64, sizeThreshold); + return; + } + + TRasterGR8P rasGR8(ras); + if (rasGR8) { + ::majorityDespeckle(rasGR8, sizeThreshold); + return; + } + + TRasterGR16P rasGR16(ras); + if (rasGR16) { + ::majorityDespeckle(rasGR16, sizeThreshold); + return; + } + + TRasterCM32P rasCM32(ras); + if (rasCM32) { + //Not yet implemented + assert(false); + + //::majorityDespeckleCM(rasCM32, sizeThreshold, toneTol); + return; + } +} diff --git a/toonz/sources/common/trop/tdistancetransform.cpp b/toonz/sources/common/trop/tdistancetransform.cpp new file mode 100644 index 0000000..0e16f62 --- /dev/null +++ b/toonz/sources/common/trop/tdistancetransform.cpp @@ -0,0 +1,301 @@ + + +#include "tropcm.h" + +// TnzCore includes +#include "traster.h" + +// boost includes +#include + +// STD includes +#include + +//#define UNIT_TEST // Enables unit testing at program startup + +//************************************************************************ +// Rationale +//************************************************************************ + +/*! + \file tdistancetransform.cpp + \brief This file implements an O(rows * cols) 2-dimensional distance + transform algorithm with customizable action on squared pixel + distance from the closest pixel. +*/ + +//************************************************************************ +// Local namespace stuff +//************************************************************************ + +namespace +{ + +/*! + \brief Given 2 parabolas with (minimal) height at centers \p a and \p b + and centers separated by distance \p d, returns the min between + \p d and the value \p x satisfying a + x^2 == b + (x - d)^2. +*/ + +unsigned int takeoverDist(unsigned int a, unsigned int b, unsigned int d) +{ + // The actual formula is: x = (h^2 + b - a) / 2h. It simplifies as follows + // using integers only. + + // NOTE: It can be proven that with integer division, x/ab == (x/a)/b. + return (b < a) ? d : tmax((d + (b - a) / d + 1) / 2, d); // Note the +1 to get the ceil +} + +//-------------------------------------------------------------- + +template +void initializeDT(const TRasterPT &ras, const TRasterPT &dtRas, + IsInsideFunc isInside) +{ + assert(ras->getLx() == dtRas->getLx() && ras->getLy() == dtRas->getLy()); + + static const unsigned int uiMax = // Due to the above takeoverDist, for + (std::numeric_limits::max)() - 2; // d == 1 + + int lx = ras->getLx(), ly = ras->getLy(); + for (int y = 0; y != ly; ++y) { + Pix *pix = ras->pixels(y), *rowEnd = pix + lx; + unsigned int *dt = dtRas->pixels(y); + + for (; pix != rowEnd; ++pix, ++dt) { + assert(*dt == 0u); + + if (!isInside(*pix)) + *dt = uiMax; + } + } +} + +//-------------------------------------------------------------- + +template +void expand(int lineLength, int linesCount, + Pix *buf, int incrPix, + int incrLine, + unsigned int *dtBuf, int dtIncrPix, + int dtIncrLine, + OutFunc outFunc) +{ + struct locals { + static void copyLine(unsigned int *dst, unsigned int *src, unsigned int *srcEnd, + int srcStride) + { + for (; src != srcEnd; src += srcStride, ++dst) + *dst = *src; + } + + static void buildRange(unsigned int *dtRef, unsigned int *dtLineEnd, + unsigned int *&dtEnd, unsigned int *&dtNewRef) + { + unsigned int d = 1, dNew = 0, // dNew at 0 to provide a consistent dtNewRef + dMax = (std::numeric_limits::max)(); // at the end - should not matter though + unsigned int *dt = dtRef + 1; + + for (; d <= dMax && dt != dtLineEnd; ++d, ++dt) // Pick larger intervals if possible + { + unsigned int newDMax = ::takeoverDist(*dtRef, *dt, d); // + if (newDMax <= dMax) { + dNew = d; + dMax = newDMax; + } + } + + dtEnd = dtRef + tmin(d, dMax); // Could end the line before (dMax < d) + dtNewRef = dtRef + dNew; + } + }; // locals + + // Allocate a buffer equivalent to a dt line. It will store the original + // dt values. Final dt values will be written directly on the dt raster. + // This is necessary since read and write intervals overlap. + boost::scoped_array dtOriginalLine(new unsigned int[lineLength]); + + unsigned int *odtLineStart = dtOriginalLine.get(), + *odtLineEnd = odtLineStart + lineLength; + + // Process each line + for (int l = 0; l != linesCount; ++l) { + unsigned int *dtLineStart = dtBuf + dtIncrLine * l, // Using dtBuf to track colors from now on, + *dtLineEnd = dtLineStart + dtIncrPix * lineLength, // it already embeds colorFunc's output due + *dt = dtLineStart, // to the way it was initialized. + *odtRef = odtLineStart; + + Pix *lineStart = buf + incrLine * l, + *pix = lineStart; + + // Make a copy of the original dt values + locals::copyLine(dtOriginalLine.get(), dtLineStart, dtLineEnd, dtIncrPix); + + // Expand a colored pixel along the line + while (dt != dtLineEnd) { + // The line is subdivided in consecutive ranges associated to the same + // half-parabola - process one + + // Build a half-parabola range + unsigned int *dtEnd, *odtNewRef; + locals::buildRange(odtRef, odtLineEnd, dtEnd, odtNewRef); + + assert(odtLineStart <= odtNewRef && odtNewRef <= odtLineEnd); + assert(odtLineStart <= dtEnd && dtEnd <= odtLineEnd); + + dtEnd = dtLineStart + dtIncrPix * (dtEnd - odtLineStart); // Convert dtEnd to the dt raster buffer + + // Process the range + Pix *ref = lineStart + incrPix * (odtRef - odtLineStart); + + unsigned int d = (pix - ref) / incrPix; + for (; dt != dtEnd; ++d, dt += dtIncrPix, pix += incrPix) + outFunc(*pix, *ref, *dt = *odtRef + sq(d)); + + odtRef = odtNewRef; + } + } +} + +//-------------------------------------------------------------- + +/*! + \brief Performs an O(rows * cols) distance transform on the specified + raster image. + + \details The algorithm relies on the separability of the 2D DT into 2 + passes (by rows and columns) of 1-dimensional DTs. + + The 1D DT sums a pre-existing (from the previous DT step if any) + DT result with the one currently calculated. + + \warning Templace parameter OutFunc is supposed to satisfy \a transitivity + upon comparison of its output - so, if \p b is the output of \p a, + and \p c is the output of \p b, then \p c is the same as the output + of \p a. + + \todo Accept a different output raster - but preserve the case where + (srcRas == dstRas). +*/ + +template +void distanceTransform(const TRasterPT &ras, IsInsideFunc isInside, OutFunc outFunc) +{ + int lx = ras->getLx(), ly = ras->getLy(); + + // Allocate a suitable temporary raster holding the (squared) distance transform + // built from the specified color function + TRasterPT dtRas(lx, ly); // Summed squared distances will be limited to + // 2 billions. This is generally suitable. + ::initializeDT(ras, dtRas, isInside); // The raster is binarized directly into the + // auxiliary dtRas. Pixels in the set to expand + // will have value 0, the others a suitable high value. + expand(lx, ly, ras->pixels(0), 1, ras->getWrap(), + dtRas->pixels(0), 1, dtRas->getWrap(), outFunc); + expand(lx, ly, ras->pixels(0) + lx - 1, -1, ras->getWrap(), + dtRas->pixels(0) + lx - 1, -1, dtRas->getWrap(), outFunc); + + expand(ly, lx, ras->pixels(0), ras->getWrap(), 1, + dtRas->pixels(0), dtRas->getWrap(), 1, outFunc); + expand(ly, lx, ras->pixels(ly - 1), -ras->getWrap(), 1, + dtRas->pixels(ly - 1), -dtRas->getWrap(), 1, outFunc); +} +} + +//************************************************************************ +// Local Functors +//************************************************************************ + +/* + Using functors here just to be absolutely sure that calls are not + callbacks. +*/ + +namespace +{ + +struct SomePaint { + inline bool operator()(const TPixelCM32 &pix) const + { + return (pix.getTone() != 0) || (pix.getPaint() != 0); + } +}; + +struct CopyPaint { + inline void operator()(TPixelCM32 &out, const TPixelCM32 &in, unsigned int) const + { + out.setPaint(in.getPaint()); + } +}; +} + +//************************************************************************ +// API functions +//************************************************************************ + +void TRop::expandPaint(const TRasterCM32P &rasCM) +{ + distanceTransform(rasCM, SomePaint(), CopyPaint()); +} + +//************************************************************************ +// Unit testing +//************************************************************************ + +#if defined UNIT_TEST && !defined NDEBUG + +namespace +{ + +void assertEqualBufs(const TRasterT &a, const TRasterT &b) +{ + for (int y = 0; y != a.getLy(); ++y) { + for (int x = 0; x != a.getLx(); ++x) + assert(a.pixels(y)[x] == b.pixels(y)[x]); + } +} + +struct Selector { + inline bool operator()(unsigned int val) const + { + return val; + } +}; + +struct OutputDT { + inline void operator()(unsigned int &out, const unsigned int &in, unsigned int d2) const + { + out = d2; + } +}; + +struct DTTest { + DTTest() + { + unsigned int imgBuf[] = { + 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + }; + + unsigned int dtBuf[] = { + 4, 1, 0, 1, 4, 5, + 2, 1, 0, 1, 1, 2, + 1, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 2, + 2, 1, 2, 4, 4, 5, + }; + + TRasterPT imgRas(6, 5, 6, imgBuf, false), + dtRas(6, 5, 6, dtBuf, false); + + distanceTransform(imgRas, Selector(), OutputDT()); + assertEqualBufs(*imgRas, *dtRas); + } +} dtTest; + +} // namespace + +#endif // UNIT_TEST && !NDEBUG diff --git a/toonz/sources/common/trop/terodilate.cpp b/toonz/sources/common/trop/terodilate.cpp new file mode 100644 index 0000000..e16de7d --- /dev/null +++ b/toonz/sources/common/trop/terodilate.cpp @@ -0,0 +1,478 @@ + + +// tcg includes +#include "tcg/tcg_misc.h" + +#include "trop.h" + +/*! \file terodilate.cpp + +This file contains an implementation of a greyscale (ie per-channel) erode/dilate +morphological operator, following the van Herk/Gil-Werman O(row*cols) algorithm. + +An extension with circular structuring element is attempted - unfortunately I could +not retrieve a copy of Miyataka's paper about that, which seemingly claimed +O(rows * cols) too. The implemented algorithm is a sub-optimal O(rows*cols*radius). +*/ + +//******************************************************** +// Auxiliary functions +//******************************************************** + +namespace +{ + +template +void copyMatte(const TRasterPT &src, const TRasterPT &matte) +{ + typedef typename Pix::Channel Chan; + + int y, lx = src->getLx(), ly = src->getLy(); + for (y = 0; y != ly; ++y) { + Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx; + Chan *m, *mBegin = matte->pixels(y); + + for (s = sBegin, m = mBegin; s != sEnd; ++s, ++m) + *m = s->m; + } +} + +//-------------------------------------------------------------- + +template +void copyChannels_erode(const TRasterPT &src, const TRasterPT &matte, + const TRasterPT &dst) +{ + typedef typename Pix::Channel Chan; + + // Just assemble src and matte, remembering to depremultiply src pixels before + // applying the new matte + + double fac; + + int y, lx = src->getLx(), ly = src->getLy(); + for (y = 0; y != ly; ++y) { + const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx; + Pix *d, *dBegin = dst->pixels(y); + + Chan *m, *mBegin = matte->pixels(y); + + for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) { + fac = double(*m) / double(s->m); + d->r = fac * s->r, d->g = fac * s->g, d->b = fac * s->b, d->m = *m; + } + } +} + +//-------------------------------------------------------------- + +template +void copyChannels_dilate(const TRasterPT &src, const TRasterPT &matte, + const TRasterPT &dst) +{ + typedef typename Pix::Channel Chan; + + // Trickier - since src is presumably premultiplied, increasing its pixels' alpha by direct + // substitution would expose the excessive RGB discretization of pixels with a low matte value. + // So, let's just put the pixels on a black background. It should do fine. + + double max = Pix::maxChannelValue; + + int y, lx = src->getLx(), ly = src->getLy(); + for (y = 0; y != ly; ++y) { + const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx; + Pix *d, *dBegin = dst->pixels(y); + + const Chan *m, *mBegin = matte->pixels(y); + + for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) { + *d = *s; + d->m = s->m + (1.0 - s->m / max) * *m; + } + } +} + +} // namespace + +//******************************************************** +// EroDilate algorithms +//******************************************************** + +namespace +{ + +template +struct MaxFunc { + inline Chan operator()(const Chan &a, const Chan &b) { return tmax(a, b); } +}; + +template +struct MinFunc { + inline Chan operator()(const Chan &a, const Chan &b) { return tmin(a, b); } +}; + +//-------------------------------------------------------------- + +// NOTE: src and dst must be NOT OVERLAPPING (eg src != dst) + +template +void erodilate_row(int len, const Chan *src, int sIncr, Chan *dst, int dIncr, + int rad, double radR, Func func) +{ + assert(rad >= 0); + + // Segment the row of specified length into wCount windows of max wSize elements + int w, wSize = 2 * rad + 1, wCount = len / wSize + 1; + int swIncr = wSize * sIncr, srIncr = rad * sIncr; + int dwIncr = wSize * dIncr, drIncr = rad * dIncr; + + const Chan *s, *sEnd = src + len * sIncr; + Chan *d, *dEnd = dst + len * dIncr; + + double one_radR = (1.0 - radR); + + for (w = 0; w != wCount; ++w) { + Chan *dwBegin = dst + w * dwIncr, *dwEnd = tmin(dwBegin + dwIncr, dEnd); + + // Compute prefixes + const Chan *swBegin = src + tmax(w * swIncr - srIncr - sIncr, 0), + *swEnd = src + tmin(w * swIncr + srIncr + sIncr, len * sIncr); + + s = swEnd - sIncr, d = dst + ((s - src) / sIncr) * dIncr + drIncr; // d already decremented by dIncr + + Chan val = *s, oldVal; + + for (s -= sIncr; (d >= dEnd) && (s >= swBegin); s -= sIncr, d -= dIncr) // s decremented here + { + assert(s >= src); + assert(s < sEnd); + assert((s - src) % sIncr == 0); + assert(d >= dst); + assert((d - dst) % dIncr == 0); + + val = func(oldVal = val, *s); + } + + for (; s >= swBegin; s -= sIncr, d -= dIncr) { + assert(s >= src); + assert(s < sEnd); + assert((s - src) % sIncr == 0); + assert(d >= dst); + assert(d < dEnd); + assert((d - dst) % dIncr == 0); + + val = func(oldVal = val, *s); + *d = (oldVal == val) ? val : one_radR * oldVal + radR * val; + } + + for (d = tmin(d, dEnd - dIncr); d >= dwBegin; d -= dIncr) { + assert(d >= dst); + assert(d < dEnd); + assert((d - dst) % dIncr == 0); + + val = func(oldVal = val, 0); + *d = (oldVal == val) ? val : one_radR * oldVal + radR * val; + } + + // Compute suffixes + swBegin = src + w * swIncr + srIncr, swEnd = tmin(swBegin + swIncr + sIncr, sEnd); + if (swBegin >= swEnd) + continue; + + s = swBegin, d = dwBegin; + + val = *s; + + for (s += sIncr; (s < swEnd); s += sIncr, d += dIncr) { + assert(s >= src); + assert(s < sEnd); + assert((s - src) % sIncr == 0); + assert(d >= dst); + assert(d < dEnd); + assert((d - dst) % dIncr == 0); + + val = func(oldVal = val, *s); + *d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val); + } + + for (; d < dwEnd; d += dIncr) { + assert(d >= dst); + assert(d < dEnd); + assert((d - dst) % dIncr == 0); + + val = func(oldVal = val, 0); + *d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val); + } + } +} + +//-------------------------------------------------------------- + +template +void erodilate_chan(const TRasterPT &src, const TRasterPT &dst, double radius, bool dilate) +{ + assert(radius > 0.0); + + int radI = tfloor(radius); + double radR = radius - radI; + + // Using a temporary raster to keep intermediate results. This allows us to + // perform a cache-friendly iteration in the separable/square kernel case + int x, y, lx = src->getLx(), ly = src->getLy(); + + // Peform rows erodilation + TRasterPT temp(ly, lx); // Notice transposition plz + + { + if (dilate) + for (y = 0; y != ly; ++y) + ::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly, radI, radR, MaxFunc()); + else + for (y = 0; y != ly; ++y) + ::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly, radI, radR, MinFunc()); + } + + // Perform columns erodilation + { + if (dilate) + for (x = 0; x != lx; ++x) + ::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x, dst->getWrap(), radI, radR, MaxFunc()); + else + for (x = 0; x != lx; ++x) + ::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x, dst->getWrap(), radI, radR, MinFunc()); + } +} + +//-------------------------------------------------------------- + +template +void rect_erodilate(const TRasterPT &src, const TRasterPT &dst, double radius) +{ + typedef typename Pix::Channel Chan; + + if (radius == 0.0) { + // No-op case + TRop::copy(dst, src); + return; + } + + bool dilate = (radius >= 0.0); + + // Perform columns erodilation + TRasterPT temp(src->getLx(), src->getLy()); + ::erodilate_chan(src, temp, fabs(radius), dilate); + + // Remember that we have just calculated the matte values. We still have to apply them to the old RGB + // values, which requires depremultiplying from source matte and premultiplying with the new one. + if (dilate) + ::copyChannels_dilate(src, temp, dst); + else + ::copyChannels_erode(src, temp, dst); +} + +} // namespace + +//******************************************************** +// EroDilate round algorithm +//******************************************************** + +namespace +{ + +template +void erodilate_quarters(int lx, int ly, + Chan *src, int sIncrX, int sIncrY, + Chan *dst, int dIncrX, int dIncrY, + double radius, double shift, Func func) +{ + double sqRadius = sq(radius); + double squareHeight = radius / tcg::consts::sqrt2; + int squareHeightI = tfloor(squareHeight); + + // For every arc point + int arcY; + for (arcY = -squareHeightI; arcY <= squareHeightI; ++arcY) { + // Calculate x and weights + double sqArcY = sq(arcY); + assert(sqRadius >= sqArcY); + + double x = shift + sqrt(sqRadius - sqArcY) - squareHeight; + + int arcX = tfloor(x); + double w = x - arcX, one_w = 1.0 - w; + + // Build dst area influenced by the arc point. Func with 0 outside that. + TRect bounds(0, 0, lx, ly); + + TRect dRect(bounds * (bounds + TPoint(-arcX, -arcY))); + TRect sRect(bounds * (bounds + TPoint(arcX, arcY))); + + int sy, dy; + + // Func with 0 before dRect.y0 + for (dy = 0; dy < dRect.y0; ++dy) { + Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX; + for (d = dBegin; d != dEnd; d += dIncrX) { + //assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0); + *d = func(*d, 0); + } + } + + // Func with 0 after dRect.y1 + for (dy = dRect.y1; dy < ly; ++dy) { + Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX; + for (d = dBegin; d != dEnd; d += dIncrX) { + //assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0); + *d = func(*d, 0); + } + } + + // For every dst pixel in the area, Func with the corresponding pixel in src + for (dy = dRect.y0, sy = sRect.y0; dy != dRect.y1; ++dy, ++sy) { + Chan *d, *dLine = dst + dy * dIncrY, *dBegin = dLine + dRect.x0 * dIncrX; + Chan *s, *sLine = src + sy * sIncrY, *sBegin = sLine + sRect.x0 * sIncrX, *sEnd = sLine + sRect.x1 * sIncrX; + + Chan *sLast = sEnd - sIncrX; // sLast would lerp with sEnd + + for (d = dBegin, s = sBegin; s != sLast; d += dIncrX, s += sIncrX) // hence we stop before it + { + //assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0); + //assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0); + + *d = func(*d, *s * one_w + *(s + sIncrX) * w); + } + + //assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0); + //assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0); + + *d = func(*d, *s * one_w); // lerp sLast with 0 + } + } +} + +//-------------------------------------------------------------- + +template +void circular_erodilate(const TRasterPT &src, const TRasterPT &dst, double radius) +{ + typedef typename Pix::Channel Chan; + + if (radius == 0.0) { + // No-op case + TRop::copy(dst, src); + return; + } + + // Ok, the idea is: consider the maximal embedded square in our circular structuring element. + // Erodilating by it consists in the consecutive erodilation by rows and columns with the same + // 'square' radius. Now, it's easy to see that the square could be 'bent' so that one of its + // edges matches that of a 1/4 of the circle's edge, while remaining inside the circle. + // Erodilating by the bent square can be achieved by erodilating first by rows or column for + // the square edge radius, followed by perpendicular erodilationg with a fourth of our + // circumference. Sum the 4 erodilations needed to complete the circumference - and it's done. + + // NOTE: Unfortunately, the above decomposition has lots of intersections among the pieces - yet + // it's simple enough and removes an O(radius) from the naive algorithm. Could be done better? + + // First, build the various erodilation data + bool dilate = (radius >= 0.0); + radius = fabs(radius); + + double inner_square_diameter = radius * tcg::consts::sqrt2; + + double shift = 0.25 * inner_square_diameter; // Shift of the bent square SE needed to avoid + // touching the circumference on the other side + double row_filter_radius = 0.5 * (inner_square_diameter - shift); + double cseShift = 0.5 * shift; // circumference structuring element shift + + int lx = src->getLx(), ly = src->getLy(); + + TRasterPT temp1(lx, ly), temp2(lx, ly); + + int radI = tfloor(row_filter_radius); + double radR = row_filter_radius - radI; + + if (dilate) { + temp2->fill(0); // Initialize with a Func-neutral value + + if (row_filter_radius > 0.0) + for (int y = 0; y != ly; ++y) + ::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI, radR, MaxFunc()); + else + ::copyMatte(src, temp1); + + ::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1, lx, radius, cseShift, MaxFunc()); + ::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx, temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift, MaxFunc()); + + if (row_filter_radius > 0.0) + for (int x = 0; x != lx; ++x) + ::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(), temp1->pixels(0) + x, lx, radI, radR, MaxFunc()); + else + ::copyMatte(src, temp1); + + ::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx, 1, radius, cseShift, MaxFunc()); + ::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1, temp2->pixels(0) + lx * ly - 1, -lx, -1, radius, cseShift, MaxFunc()); + } else { + temp2->fill((std::numeric_limits::max)()); // Initialize with a Func-neutral value + + if (row_filter_radius > 0.0) + for (int y = 0; y != ly; ++y) + ::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI, radR, MinFunc()); + else + ::copyMatte(src, temp1); + + ::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1, lx, radius, cseShift, MinFunc()); + ::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx, temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift, MinFunc()); + + if (row_filter_radius > 0.0) + for (int x = 0; x != lx; ++x) + ::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(), temp1->pixels(0) + x, lx, radI, radR, MinFunc()); + else + ::copyMatte(src, temp1); + + ::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx, 1, radius, cseShift, MinFunc()); + ::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1, temp2->pixels(0) + lx * ly - 1, -lx, -1, radius, cseShift, MinFunc()); + } + + // Remember that we have just calculated the matte values. We still have to apply them to the old RGB + // values, which requires depremultiplying from source matte and premultiplying with the new one. + if (dilate) + ::copyChannels_dilate(src, temp2, dst); + else + ::copyChannels_erode(src, temp2, dst); +} + +} // namespace + +//******************************************************** +// EroDilate main functions +//******************************************************** + +void TRop::erodilate(const TRasterP &src, const TRasterP &dst, + double radius, ErodilateMaskType type) +{ + assert(src->getSize() == dst->getSize()); + + src->lock(), dst->lock(); + + if ((TRaster32P)src && (TRaster32P)dst) + switch (type) { + case ED_rectangular: + ::rect_erodilate(src, dst, radius); + CASE ED_circular : ::circular_erodilate(src, dst, radius); + DEFAULT: + assert(!"Unknown mask type"); + } + else if ((TRaster64P)src && (TRaster64P)dst) + switch (type) { + case ED_rectangular: + ::rect_erodilate(src, dst, radius); + CASE ED_circular : ::circular_erodilate(src, dst, radius); + DEFAULT: + assert(!"Unknown mask type"); + } + else + assert(!"Unsupported raster type!"); + + src->unlock(), dst->unlock(); +} diff --git a/toonz/sources/common/trop/tfracmove.cpp b/toonz/sources/common/trop/tfracmove.cpp new file mode 100644 index 0000000..06e2a80 --- /dev/null +++ b/toonz/sources/common/trop/tfracmove.cpp @@ -0,0 +1,118 @@ + + +//#include "trop.h" +#include "tpalette.h" +#include "tropcm.h" + +//------------------------------------------------------------------------------ + +namespace +{ + +inline double gauss(double x, double y, double x0, double y0, double s) +{ + return exp(-((x - x0) * (x - x0) + (y - y0) * (y - y0)) / s) / (s * TConsts::pi); +} + +//------------------------------------------------------------------------------ + +double integ_gauss(double xmin, double ymin, double xmax, double ymax, + double x0, double y0, double s) +{ + int i, j, n; + double x1, y1, x2, y2, xstep, ystep, area; + double sum; + n = 50; + sum = 0; + xstep = (xmax - xmin) / n; + ystep = (ymax - ymin) / n; + area = xstep * ystep; + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) { + x1 = xmin + xstep * i; + y1 = ymin + ystep * i; + x2 = x1 + xstep; + y2 = y1 + ystep; + sum += area * (gauss(x1, y1, x0, y0, s) + + gauss(x1, y2, x0, y0, s) + + gauss(x2, y2, x0, y0, s) + + gauss(x2, y1, x0, y0, s)) / + 4; + } + return sum; +} + +//------------------------------------------------------------------------------ + +void build_filter(double h[], double x0, double y0, double s) +{ + double sum, vv; + int i, j, k; + double x1, y1, x2, y2; + k = 0; + sum = 0.0; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) { + x1 = -1.5 + j; + y1 = -1.5 + i; + x2 = x1 + 1.0; + y2 = y1 + 1.0; + h[k++] = vv = integ_gauss(x1, y1, x2, y2, x0, y0, s); + sum += vv; + } + if (sum > 0.0) { + sum = 1.0 / sum; + for (k = 0; k < 9; k++) + h[k] *= sum; + } +} + +} // anonymous namespace + +//------------------------------------------------------------------------------ + +void TRop::fracmove(TRasterP rout, TRasterP rin, double dx, double dy) +{ + //Using a bilinear filter is best - consider that only 4 pixels should contribute + //to a fractionarily shifted one, with weights proportional to the intersection areas. + double w[4] = {1, 0, 0, 0}; + double sum = 0; + + int idx = tfloor(dx); + int idy = tfloor(dy); + double fracX = dx - idx; + double fracY = dy - idy; + + int i, j; + for (i = 0; i < 2; ++i) + for (j = 0; j < 2; ++j) + sum += w[j + 2 * i] = fabs(fracX - j) * fabs(fracY - i); + + for (i = 0; i < 4; ++i) + w[i] /= sum; + + TRop::convolve_i(rout, rin, idx, idy, w, 2); +} + +//------------------------------------------------------------------------------ + +void TRop::fracmove(TRasterP rout, TRasterCM32P rin, const TPaletteP &palette, double dx, double dy) +{ + double w[4] = {1, 0, 0, 0}; + double sum = 0; + + int idx = tfloor(dx); + int idy = tfloor(dy); + double fracX = dx - idx; + double fracY = dy - idy; + + int i, j; + for (i = 0; i < 2; ++i) + for (j = 0; j < 2; ++j) + sum += w[j + 2 * i] = fabs(fracX - j) * fabs(fracY - i); + + for (i = 0; i < 4; ++i) + w[i] /= sum; + + TRop::convolve_i(rout, rin, idx, idy, w, 2); +} diff --git a/toonz/sources/common/trop/tinvert.cpp b/toonz/sources/common/trop/tinvert.cpp new file mode 100644 index 0000000..14c3e22 --- /dev/null +++ b/toonz/sources/common/trop/tinvert.cpp @@ -0,0 +1,121 @@ + + +#include "trop.h" +#include "tpixelgr.h" + +namespace +{ +template +inline void do_invert(TRasterPT ras) +{ + int wrap = ras->getWrap(); + int lx = ras->getLx(); + PixType *rowIn = ras->pixels(); + PixType *lastPix = rowIn + wrap * ras->getLy(); + PixType *pixIn = 0; + PixType *endPix = 0; + + while (pixIn < lastPix) { + pixIn = rowIn; + endPix = pixIn + lx; + while (pixIn < endPix) { + pixIn->r = pixIn->m - pixIn->r; + pixIn->g = pixIn->m - pixIn->g; + pixIn->b = pixIn->m - pixIn->b; /*pixIn->m = pixIn->m;*/ + + ++pixIn; + } + rowIn += wrap; + } +} +//------------------------------------------------------------------------------ + +template +inline void do_invert(TRasterPT ras, bool invRed, bool invGreen, bool invBlue, bool invMatte) +{ + int wrap = ras->getWrap(); + int lx = ras->getLx(); + PixType *rowIn = ras->pixels(); + PixType *lastPix = rowIn + wrap * ras->getLy(); + PixType *pixIn = 0; + PixType *endPix = 0; + + while (pixIn < lastPix) { + pixIn = rowIn; + endPix = pixIn + lx; + while (pixIn < endPix) { + if (invRed) + pixIn->r = pixIn->m - pixIn->r; + if (invGreen) + pixIn->g = pixIn->m - pixIn->g; + if (invBlue) + pixIn->b = pixIn->m - pixIn->b; + if (invMatte) + pixIn->m = ~pixIn->m; + ++pixIn; + } + rowIn += wrap; + } +} + +//------------------------------------------------------------------------------ + +template <> +inline void do_invert(TRasterPT ras) +{ + int wrap = ras->getWrap(); + int lx = ras->getLx(); + TPixelGR8 *rowIn = ras->pixels(); + TPixelGR8 *lastPix = rowIn + wrap * ras->getLy(); + TPixelGR8 *pixIn = 0; + TPixelGR8 *endPix = 0; + + while (pixIn < lastPix) { + pixIn = rowIn; + endPix = pixIn + lx; + while (pixIn < endPix) { + pixIn->value = 255 - pixIn->value; + + ++pixIn; + } + rowIn += wrap; + } +} +} + +//------------------------------------------------------------------------------ + +void TRop::invert(TRasterP ras, bool invRed, bool invGreen, bool invBlue, bool invMatte) +{ + if (!invRed && !invGreen && !invBlue) + return; + bool flag = invRed && invGreen && invBlue && !invMatte; + + TRaster32P ras32 = ras; + ras->lock(); + if (ras32) + if (flag) + do_invert(ras32); + else + do_invert(ras, invRed, invGreen, invBlue, invMatte); + else { + TRaster64P ras64 = ras; + if (ras64) + if (flag) + do_invert(ras64); + else + do_invert(ras64, invRed, invGreen, invBlue, invMatte); + else { + TRasterGR8P ras8 = ras; + if (ras8) + do_invert(ras8); + else { + ras->unlock(); + throw TRopException("unsupported pixel type"); + } + } + } + ras->unlock(); +} + +//------------------------------------------------------------------------------ diff --git a/toonz/sources/common/trop/toperators.cpp b/toonz/sources/common/trop/toperators.cpp new file mode 100644 index 0000000..4ef1fc8 --- /dev/null +++ b/toonz/sources/common/trop/toperators.cpp @@ -0,0 +1,1628 @@ + + +#include "trop.h" +#include "tpixel.h" +#include "tpixelutils.h" + +#ifdef WIN32 +#include // per SSE2 +#endif + +namespace +{ +inline double luminance(TPixel32 *pix) +{ + return 0.2126 * pix->r + 0.7152 * pix->g + 0.0722 * pix->b; +} +inline double luminance(TPixel64 *pix) +{ + return 0.2126 * pix->r + 0.7152 * pix->g + 0.0722 * pix->b; +} +} //namespace + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_BEGIN_LOOP(UpType, up, DownType, down, OutType, out) \ + { \ + int upWrap = up->getWrap(); \ + int downWrap = down->getWrap(); \ + int outWrap = out->getWrap(); \ + \ + up->lock(); \ + down->lock(); \ + out->lock(); \ + UpType *upPix = 0, *upRow = up->pixels(); \ + DownType *downPix, *downRow = down->pixels(); \ + OutType *outPix, *outRow = out->pixels(); \ + UpType *endPix; \ + int upLx = up->getLx(); \ + UpType *lastPix = upRow + upWrap * (up->getLy() - 1) + upLx; \ + while (upPix < lastPix) { \ + upPix = upRow; \ + downPix = downRow; \ + outPix = outRow; \ + endPix = upPix + upLx; \ + while (upPix < endPix) { + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_END_LOOP(up, down, out) \ + ++upPix; \ + ++downPix; \ + ++outPix; \ + } \ + upRow += upWrap; \ + downRow += downWrap; \ + outRow += outWrap; \ + } \ + up->unlock(); \ + down->unlock(); \ + out->unlock(); \ + } + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_32_BEGIN_LOOP \ + assert(up32 &&down32 &&out32); \ + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM32, up32, TPixelRGBM32, down32, TPixelRGBM32, out32) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_32_END_LOOP \ + assert(up32 &&down32 &&out32); \ + FOR_EACH_PIXEL_END_LOOP(up32, down32, out32) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_64_BEGIN_LOOP \ + assert(up64 &&down64 &&out64); \ + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM64, up64, TPixelRGBM64, down64, TPixelRGBM64, out64) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_64_END_LOOP \ + assert(up64 &&down64 &&out64); \ + FOR_EACH_PIXEL_END_LOOP(up64, down64, out64) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_8_BEGIN_LOOP \ + assert(up8 &&down8 &&out8); \ + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelGR8, up8, TPixelGR8, down8, TPixelGR8, out8) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_8_END_LOOP \ + assert(up8 &&down8 &&out8); \ + FOR_EACH_PIXEL_END_LOOP(up32, down32, out32) + +//----------------------------------------------------------------------------- + +void TRop::add(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, double v) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + TINT32 r, g, b, m; + if (upPix->m == 0) + *outPix = *downPix; + else { + r = downPix->r + tround(upPix->r * v); + g = downPix->g + tround(upPix->g * v); + b = downPix->b + tround(upPix->b * v); + m = downPix->m + tround(upPix->m * v); + outPix->r = (UCHAR)tcrop(r, (TINT32)0, (TINT32)255); + outPix->g = (UCHAR)tcrop(g, (TINT32)0, (TINT32)255); + outPix->b = (UCHAR)tcrop(b, (TINT32)0, (TINT32)255); + outPix->m = (UCHAR)tcrop(m, (TINT32)0, (TINT32)255); + } + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TINT32 r, g, b, m; + r = downPix->r + tround(upPix->r * v); + g = downPix->g + tround(upPix->g * v); + b = downPix->b + tround(upPix->b * v); + m = downPix->m + tround(upPix->m * v); + + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + USHORT value = troundp(upPix->value * v) + downPix->value; + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::add invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::add(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + USHORT r, g, b, m; + r = downPix->r + upPix->r; + g = downPix->g + upPix->g; + b = downPix->b + upPix->b; + m = downPix->m + upPix->m; + + outPix->r = (UCHAR)tcrop(r, 0, 255); + outPix->g = (UCHAR)tcrop(g, 0, 255); + outPix->b = (UCHAR)tcrop(b, 0, 255); + outPix->m = (UCHAR)tcrop(m, 0, 255); + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TINT32 r, g, b, m; + r = downPix->r + upPix->r; + g = downPix->g + upPix->g; + b = downPix->b + upPix->b; + m = downPix->m + upPix->m; + + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + USHORT value = upPix->value + downPix->value; + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::add invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::colordodge(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + if (up32 && down32 && out32) { + + FOR_EACH_PIXEL_32_BEGIN_LOOP + + USHORT r, g, b, m; + + r = (USHORT)((downPix->r << 8) / (256.0 - upPix->r)); + g = (USHORT)((downPix->g << 8) / (256.0 - upPix->g)); + b = (USHORT)((downPix->b << 8) / (256.0 - upPix->b)); + + m = downPix->m + upPix->m; + + outPix->r = (UCHAR)tcrop(r, 0, 255); + outPix->g = (UCHAR)tcrop(g, 0, 255); + outPix->b = (UCHAR)tcrop(b, 0, 255); + outPix->m = (UCHAR)tcrop(m, 0, 255); + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TINT32 r, g, b, m; + r = (TINT32)(65536.0 * (downPix->r / (65536.0 - upPix->r))); + + g = (TINT32)(65536.0 * (downPix->g / (65536.0 - upPix->g))); + + b = (TINT32)(65536.0 * (downPix->b / (65536.0 - upPix->b))); + + m = downPix->m + upPix->m; + + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + USHORT value; + if (downPix->value) + value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::color dodge invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::colorburn(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + if (up32 && down32 && out32) { + + FOR_EACH_PIXEL_32_BEGIN_LOOP + // downPix->r=0; + // downPix->g=255; + // downPix->b=0; + double r, g, b; + if (upPix->m) { + if (downPix->r == 0 || downPix->r == 255) + r = downPix->r; + else if (upPix->r) + r = 255 - (((255 - downPix->r) << 8) / (double)upPix->r); + else + r = 0; + if (downPix->g == 0 || downPix->g == 255) + g = downPix->g; + else if (upPix->g) + g = 255 - (((255 - downPix->g) << 8) / (double)upPix->g); + else + g = 0; + if (downPix->b == 0 || downPix->b == 255) + b = downPix->b; + else if (upPix->b) + b = 255 - (((255 - downPix->b) << 8) / (double)upPix->b); + else + b = 0; + + if (upPix->m != 255) { + + TPixel32 tmpPix; + tmpPix.r = (UCHAR)tcrop(r, .0, 255.0); + tmpPix.g = (UCHAR)tcrop(g, .0, 255.0); + tmpPix.b = (UCHAR)tcrop(b, .0, 255.0); + tmpPix.m = upPix->m; + ; + overPix(*outPix, *downPix, tmpPix); + } else { + outPix->r = (UCHAR)tcrop(r, .0, 255.0); + outPix->g = (UCHAR)tcrop(g, .0, 255.0); + outPix->b = (UCHAR)tcrop(b, .0, 255.0); + outPix->m = downPix->m; + } + } else { + outPix = downPix; + } + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + double r, g, b; + if (upPix->m) { + if (downPix->r == 0 || downPix->r == 65535) + r = downPix->r; + else if (upPix->r) + r = 65535 - 65536 * ((65535.0 - downPix->r) / (double)upPix->r); + else + r = 0; + if (downPix->g == 0 || downPix->g == 65535) + g = downPix->g; + else if (upPix->g) + g = 65535 - 65536 * ((65535.0 - downPix->g) / (double)upPix->g); + else + g = 0; + if (downPix->b == 0 || downPix->b == 65535) + b = downPix->b; + else if (upPix->b) + b = 65535 - 65536 * ((65535.0 - downPix->b) / (double)upPix->b); + else + b = 0; + + if (upPix->m != 65535) { + + TPixel64 tmpPix; + tmpPix.r = (USHORT)tcrop(r, .0, 65535.0); + tmpPix.g = (USHORT)tcrop(g, .0, 65535.0); + tmpPix.b = (USHORT)tcrop(b, .0, 65535.0); + tmpPix.m = upPix->m; + overPix(*outPix, *downPix, tmpPix); + } else { + outPix->r = (USHORT)tcrop(r, .0, 65535.0); + outPix->g = (USHORT)tcrop(g, .0, 65535.0); + outPix->b = (USHORT)tcrop(b, .0, 65535.0); + outPix->m = downPix->m; + } + } else { + outPix = downPix; + } + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::color burn invalid raster combination"); + } +} + +//----------------------------------------------------------------------------- + +void TRop::screen(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + if (up32 && down32 && out32) { + + FOR_EACH_PIXEL_32_BEGIN_LOOP + + double r, g, b; + r = 256 - ((256 - upPix->r) * (256 - downPix->r) >> 8); + g = 256 - ((256 - upPix->g) * (256 - downPix->g) >> 8); + b = 256 - ((256 - upPix->b) * (256 - downPix->b) >> 8); + + if (upPix->m != 255) { + double m; + m = 256 - ((256 - upPix->m) * (256 - downPix->m) >> 8); + TPixel32 tmpPix; + tmpPix.r = (UCHAR)tcrop(r, 0, 255); + tmpPix.g = (UCHAR)tcrop(g, 0, 255); + tmpPix.b = (UCHAR)tcrop(b, 0, 255); + tmpPix.m = (UCHAR)tcrop(m, 0, 255); + overPix(*outPix, *downPix, tmpPix); + } else { + + outPix->r = (UCHAR)tcrop(r, 0, 255); + outPix->g = (UCHAR)tcrop(g, 0, 255); + outPix->b = (UCHAR)tcrop(b, 0, 255); + outPix->m = upPix->m; + } + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + double r, g, b; + r = 65536 - (65536 - upPix->r) * ((65536 - downPix->r) / 65536.0); + g = 65536 - (65536 - upPix->g) * ((65536 - downPix->g) / 65536.0); + b = 65536 - (65536 - upPix->b) * ((65536 - downPix->b) / 65536.0); + + if (upPix->m != 65535) { + double m; + m = 65536 - (65536 - upPix->m) * ((65536 - downPix->m) / 65536.0); + TPixel64 tmpPix; + tmpPix.r = (USHORT)tcrop(r, 0, 65535); + tmpPix.g = (USHORT)tcrop(g, 0, 65535); + tmpPix.b = (USHORT)tcrop(b, 0, 65535); + tmpPix.m = (USHORT)tcrop(m, 0, 65535); + overPix(*outPix, *downPix, tmpPix); + } else { + + outPix->r = (USHORT)tcrop(r, 0, 65535); + outPix->g = (USHORT)tcrop(g, 0, 65535); + outPix->b = (USHORT)tcrop(b, 0, 65535); + outPix->m = upPix->m; + } + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + USHORT value; + if (downPix->value) + value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::color dodge invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::sub(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, bool matte) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + if (matte) { + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + SHORT r = downPix->r - upPix->r; + SHORT g = downPix->g - upPix->g; + SHORT b = downPix->b - upPix->b; + SHORT m = downPix->m - upPix->m; + + outPix->r = (UCHAR)tcrop(r, 0, 255); + outPix->g = (UCHAR)tcrop(g, 0, 255); + outPix->b = (UCHAR)tcrop(b, 0, 255); + outPix->m = (UCHAR)tcrop(m, 0, 255); + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TINT32 r = downPix->r - upPix->r; + TINT32 g = downPix->g - upPix->g; + TINT32 b = downPix->b - upPix->b; + TINT32 m = downPix->m - upPix->m; + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + SHORT value = upPix->value - downPix->value; + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::sub invalid raster combination"); + } + } + } else { + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + SHORT r = downPix->r - upPix->r; + SHORT g = downPix->g - upPix->g; + SHORT b = downPix->b - upPix->b; + SHORT m = downPix->m; // - upPix->m; + + outPix->r = (UCHAR)tcrop(r, 0, 255); + outPix->g = (UCHAR)tcrop(g, 0, 255); + outPix->b = (UCHAR)tcrop(b, 0, 255); + outPix->m = (UCHAR)tcrop(m, 0, 255); + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TINT32 r = downPix->r - upPix->r; + TINT32 g = downPix->g - upPix->g; + TINT32 b = downPix->b - upPix->b; + TINT32 m = downPix->m; // - upPix->m; + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + SHORT value = upPix->value - downPix->value; + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::sub invalid raster combination"); + } + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::mult(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, int v, bool matte) +{ + /* + Let U be 'up', D be 'down' and M be 'multiplied' (the result), and suppose for the moment + that pixels are both NOT premultiplied and normalized to [0, 1]. + + The additional value v is used to add to RGB components of the U pixel: + + U'_rgb = (U_rgb + v / 255) + + The matte component is either U_m D_m in case (matte == true), or D_m in case (matte == false). + Please, observe that in the case (matte == false) that choice makes the product NOT COMMUTATIVE, + but I think that's justified - it's simply the most obvious use case. + + In case (matte == true), each channel is multiplied independently, and that's it. + + The matter is more complicated when (matte == false). The problem in this case is dealing with rgb + components when U and D both have some transparency. When U is fully transparent, we expect the + result to be D, and vice-versa, which is non-trivial. + + We REQUIRE that (let's denote r only here): + + M_r = M_r_u = U_r (1 - D_m) + U_r D_r D_m, when U_m == 1 (When U is fully opaque, M_r is a D_m-linear + combination of U_r and U_r D_r) + M_r = M_r_d = D_r (1 - U_m) + U_r D_r U_m, when D_m == 1 (Vice-versa, when it's D that is fully opaque) + + Finally, we're building a weighted sum, by U_m and D_m of the two above: + + M_r = (M_r_u * U_m + M_r_d * D_m) / (U_m + D_m) = + = (...) = + = [ U_r U_m (1 - D_m) + D_r D_m (1 - U_m) + 2 U_r U_m D_r D_m ] / (U_m + D_m) + */ + + // 32-bit images case + TRaster32P up32 = rup, + down32 = rdown, + out32 = rout; + + if (up32 && down32 && out32) { + static const float maxChannelF = float(TPixel32::maxChannelValue); + static const UCHAR maxChannelC = UCHAR(TPixel32::maxChannelValue); + + float vf = v; + + if (matte) { + float dnMf, upMf_norm, outMf; + + FOR_EACH_PIXEL_32_BEGIN_LOOP // Awful... should be explicit... + + dnMf = downPix->m; + upMf_norm = upPix->m / maxChannelF; + + outMf = downPix->m * upMf_norm; + + outPix->r = tcrop((upPix->r / upMf_norm + vf) * (downPix->r / dnMf), 0.0f, outMf); + outPix->g = tcrop((upPix->g / upMf_norm + vf) * (downPix->g / dnMf), 0.0f, outMf); + outPix->b = tcrop((upPix->b / upMf_norm + vf) * (downPix->b / dnMf), 0.0f, outMf); + outPix->m = outMf; + + // NOTE: + 0.5f in the crop arguments could take care of rounding... + + FOR_EACH_PIXEL_32_END_LOOP + } else { + float umf_norm, dmf_norm, umdmf_norm, outMf; + float mSumf, uf, df, ufdf, normalizer; + + FOR_EACH_PIXEL_32_BEGIN_LOOP + + mSumf = upPix->m + float(downPix->m); + if (mSumf > 0.0f) { + umf_norm = upPix->m / maxChannelF, dmf_norm = downPix->m / maxChannelF; + outMf = upPix->m + (1.0f - umf_norm) * downPix->m; // umf_norm should be ensured in [0.0, 1.0]. + // Convex combination should be in the conversion range. + normalizer = outMf / (maxChannelF * mSumf); + umdmf_norm = umf_norm * dmf_norm; + + uf = upPix->r + vf * umdmf_norm, df = downPix->r, ufdf = uf * df; + outPix->r = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0f, outMf); + + uf = upPix->g + vf * umdmf_norm, df = downPix->g, ufdf = uf * df; + outPix->g = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0f, outMf); + + uf = upPix->b + vf * umdmf_norm, df = downPix->b, ufdf = uf * df; + outPix->b = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0f, outMf); + + outPix->m = outMf; + } else + *outPix = TPixel32::Transparent; + + FOR_EACH_PIXEL_32_END_LOOP + } + + return; + } + + // 64-bit images case + TRaster64P up64 = rup, + down64 = rdown, + out64 = rout; + + if (up64 && down64 && out64) { + static const double maxChannelF = double(TPixel64::maxChannelValue); + static const USHORT maxChannelC = USHORT(TPixel64::maxChannelValue); + + double vf = v * (TPixel64::maxChannelValue / double(TPixel32::maxChannelValue)); + + if (matte) { + double dnMf, upMf_norm, outMf; + + FOR_EACH_PIXEL_64_BEGIN_LOOP + + dnMf = downPix->m; + upMf_norm = upPix->m / maxChannelF; + + outMf = downPix->m * upMf_norm; + + outPix->r = tcrop((upPix->r / upMf_norm + vf) * (downPix->r / dnMf), 0.0, outMf); + outPix->g = tcrop((upPix->g / upMf_norm + vf) * (downPix->g / dnMf), 0.0, outMf); + outPix->b = tcrop((upPix->b / upMf_norm + vf) * (downPix->b / dnMf), 0.0, outMf); + outPix->m = outMf; + + FOR_EACH_PIXEL_64_END_LOOP + } else { + double umf_norm, dmf_norm, umdmf_norm, outMf; + double mSumf, uf, df, ufdf, normalizer; + + FOR_EACH_PIXEL_64_BEGIN_LOOP + + mSumf = upPix->m + double(downPix->m); + if (mSumf > 0.0) { + umf_norm = upPix->m / maxChannelF, dmf_norm = downPix->m / maxChannelF; + outMf = upPix->m + (1.0 - umf_norm) * downPix->m; + + normalizer = outMf / (maxChannelF * mSumf); + umdmf_norm = umf_norm * dmf_norm; + + uf = upPix->r + vf * umdmf_norm, df = downPix->r, ufdf = uf * df; + outPix->r = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0, outMf); + + uf = upPix->g + vf * umdmf_norm, df = downPix->g, ufdf = uf * df; + outPix->g = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0, outMf); + + uf = upPix->b + vf * umdmf_norm, df = downPix->b, ufdf = uf * df; + outPix->b = tcrop((uf * (maxChannelC - downPix->m) + df * (maxChannelC - upPix->m) + ufdf + ufdf) * normalizer, 0.0, outMf); + + outPix->m = outMf; + } else + *outPix = TPixel64::Transparent; + + FOR_EACH_PIXEL_64_END_LOOP + } + + return; + } + + // According to the specifics, throw an exception. I think it's not appropriate, though. + throw TRopException("TRop::mult invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::ropin(const TRasterP &source, const TRasterP &matte, const TRasterP &rout) +{ + TRaster32P source32 = source; + TRaster32P matte32 = matte; + TRaster32P out32 = rout; + TRaster64P source64 = source; + TRaster64P matte64 = matte; + TRaster64P out64 = rout; + + if (source32 && matte32 && out32) { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM32, source32, TPixelRGBM32, matte32, TPixelRGBM32, out32) + + if (downPix->m == 0) + outPix->r = outPix->g = outPix->b = outPix->m = 0; + else if (downPix->m == 255) + *outPix = *upPix; + else { + /* + __m128i zeros = _mm_setzero_si128(); + + __m128i upPix_packed_i= _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD*)upPix), zeros); + __m128 upPix_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(upPix_packed_i, zeros)); + + float fac = downPix->m / 255.0; + __m128 fac_packed = _mm_load1_ps(&fac); + + upPix_packed = _mm_mul_ps(upPix_packed, fac_packed); + + __m128i outPix_packed_i = _mm_cvtps_epi32(upPix_packed); + outPix_packed_i = _mm_packs_epi32(outPix_packed_i, zeros); + outPix_packed_i = _mm_packus_epi16(outPix_packed_i, zeros); + *(DWORD*)(outPix) = _mm_cvtsi128_si32(outPix_packed_i); +*/ + + const int MAGICFAC = (257U * 256U + 1U); + UINT fac = MAGICFAC * downPix->m; + + outPix->r = (UINT)(upPix->r * fac + (1U << 23)) >> 24; + outPix->g = (UINT)(upPix->g * fac + (1U << 23)) >> 24; + outPix->b = (UINT)(upPix->b * fac + (1U << 23)) >> 24; + outPix->m = (UINT)(upPix->m * fac + (1U << 23)) >> 24; + } + + FOR_EACH_PIXEL_END_LOOP(source32, matte32, out32) + } else if (source64 && matte64 && out64) + + { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM64, source64, TPixelRGBM64, matte64, TPixelRGBM64, out64) + + if (downPix->m == 0) + outPix->r = outPix->g = outPix->b = outPix->m = 0; + else if (downPix->m == 65535) + *outPix = *upPix; + else { + /* + __m128i zeros = _mm_setzero_si128(); + + __m128i upPix_packed_i= _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD*)upPix), zeros); + __m128 upPix_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(upPix_packed_i, zeros)); + + float fac = downPix->m / 255.0; + __m128 fac_packed = _mm_load1_ps(&fac); + + upPix_packed = _mm_mul_ps(upPix_packed, fac_packed); + + __m128i outPix_packed_i = _mm_cvtps_epi32(upPix_packed); + outPix_packed_i = _mm_packs_epi32(outPix_packed_i, zeros); + outPix_packed_i = _mm_packus_epi16(outPix_packed_i, zeros); + *(DWORD*)(outPix) = _mm_cvtsi128_si32(outPix_packed_i); +*/ + double fac = downPix->m / 65535.0; + + outPix->r = (USHORT)(upPix->r * fac); + outPix->g = (USHORT)(upPix->g * fac); + outPix->b = (USHORT)(upPix->b * fac); + outPix->m = (USHORT)(upPix->m * fac); + } + + FOR_EACH_PIXEL_END_LOOP(source64, matte64, out64) + } else + throw TRopException("TRop::in invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::ropout(const TRasterP &source, const TRasterP &matte, const TRasterP &rout) +{ + TRaster32P source32 = source; + TRaster32P matte32 = matte; + TRaster32P out32 = rout; + TRaster64P source64 = source; + TRaster64P matte64 = matte; + TRaster64P out64 = rout; + + if (source32 && matte32 && out32) { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM32, source32, TPixelRGBM32, matte32, TPixelRGBM32, out32) + + if (downPix->m == 255) + outPix->r = outPix->g = outPix->b = outPix->m = 0; + else if (downPix->m == 0) + *outPix = *upPix; + else { + const int MAGICFAC = (257U * 256U + 1U); + UINT fac = MAGICFAC * (255 - downPix->m); + + outPix->r = (UINT)(upPix->r * fac + (1U << 23)) >> 24; + outPix->g = (UINT)(upPix->g * fac + (1U << 23)) >> 24; + outPix->b = (UINT)(upPix->b * fac + (1U << 23)) >> 24; + outPix->m = (UINT)(upPix->m * fac + (1U << 23)) >> 24; + } + + FOR_EACH_PIXEL_END_LOOP(source32, matte32, out32) + } else if (source64 && matte64 && out64) { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM64, source64, TPixelRGBM64, matte64, TPixelRGBM64, out64) + + if (downPix->m == 65535) + outPix->r = outPix->g = outPix->b = outPix->m = 0; + else if (downPix->m == 0) + *outPix = *upPix; + else { + double fac = (65535 - downPix->m) / 65535.0; + + outPix->r = (USHORT)(upPix->r * fac); + outPix->g = (USHORT)(upPix->g * fac); + outPix->b = (USHORT)(upPix->b * fac); + outPix->m = (USHORT)(upPix->m * fac); + } + + FOR_EACH_PIXEL_END_LOOP(source64, matte64, out64) + } else + throw TRopException("TRop::out invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::atop(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + // calcola rup ATOP rdown + + // da ottimizzare... + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + TPixel32 tmpPix(0, 0, 0, 0); + if (downPix->m != 0) { + const int MAGICFAC = (257U * 256U + 1U); + UINT fac = MAGICFAC * downPix->m; + + tmpPix.r = (UINT)(upPix->r * fac + (1U << 23)) >> 24; + tmpPix.g = (UINT)(upPix->g * fac + (1U << 23)) >> 24; + tmpPix.b = (UINT)(upPix->b * fac + (1U << 23)) >> 24; + tmpPix.m = (UINT)(upPix->m * fac + (1U << 23)) >> 24; + } + + overPix(*outPix, *downPix, tmpPix); + + FOR_EACH_PIXEL_32_END_LOOP + } else if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TPixel64 tmpPix(0, 0, 0, 0); + if (downPix->m != 0) { + double fac = downPix->m / 65535.0; + + tmpPix.r = (USHORT)(upPix->r * fac); + tmpPix.g = (USHORT)(upPix->g * fac); + tmpPix.b = (USHORT)(upPix->b * fac); + tmpPix.m = (USHORT)(upPix->m * fac); + } + + overPix(*outPix, *downPix, tmpPix); + + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::atop invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::txor(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + // da ottimizzare... + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + TUINT32 notUpM = 255 - upPix->m; + TUINT32 notDownM = 255 - downPix->m; + + TUINT32 r = notUpM + upPix->r * (notDownM); + TUINT32 g = notUpM + upPix->g * (notDownM); + TUINT32 b = notUpM + upPix->b * (notDownM); + + outPix->r = (UCHAR)tcrop(0, 255, r); + outPix->g = (UCHAR)tcrop(0, 255, g); + outPix->b = (UCHAR)tcrop(0, 255, b); + + FOR_EACH_PIXEL_32_END_LOOP + } else if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + TUINT32 notUpM = 65535 - upPix->m; + TUINT32 notDownM = 65535 - downPix->m; + + TUINT32 r = notUpM + upPix->r * (notDownM); + TUINT32 g = notUpM + upPix->g * (notDownM); + TUINT32 b = notUpM + upPix->b * (notDownM); + + outPix->r = (USHORT)tcrop(0, 65535, r); + outPix->g = (USHORT)tcrop(0, 65535, g); + outPix->b = (USHORT)tcrop(0, 65535, b); + + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::xor invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::crossDissolve(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, UCHAR v) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + outPix->r = (upPix->r * v + downPix->r * (255 - v)) / 255; + outPix->g = (upPix->g * v + downPix->g * (255 - v)) / 255; + outPix->b = (upPix->b * v + downPix->b * (255 - v)) / 255; + outPix->m = (upPix->m * v + downPix->m * (255 - v)) / 255; + + FOR_EACH_PIXEL_32_END_LOOP + } else if (up64 && down64 && out64) { + USHORT vv = v * 257; + + FOR_EACH_PIXEL_64_BEGIN_LOOP + + outPix->r = (upPix->r * vv + downPix->r * (65535 - vv)) / 65535; + outPix->g = (upPix->g * vv + downPix->g * (65535 - vv)) / 65535; + outPix->b = (upPix->b * vv + downPix->b * (65535 - vv)) / 65535; + outPix->m = (upPix->m * vv + downPix->m * (65535 - vv)) / 65535; + + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::crossDissolve invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::darken(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + double value0 = luminance(upPix); + double value1 = luminance(downPix); + + if (value0 < value1) + *outPix = *upPix; + else + *outPix = *downPix; + + FOR_EACH_PIXEL_32_END_LOOP + } else if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + double value0 = luminance(upPix); + double value1 = luminance(downPix); + + if (value0 < value1) + *outPix = *upPix; + else + *outPix = *downPix; + + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::darken invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::lighten(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + double value0 = luminance(upPix); + double value1 = luminance(downPix); + + if (value0 > value1) { + TINT32 r, g, b, m; + if (upPix->m == 0) + *outPix = *downPix; + else { + r = downPix->r + upPix->r; + g = downPix->g + upPix->g; + b = downPix->b + upPix->b; + m = downPix->m + upPix->m; + outPix->r = (UCHAR)tcrop(r, (TINT32)0, (TINT32)255); + outPix->g = (UCHAR)tcrop(g, (TINT32)0, (TINT32)255); + outPix->b = (UCHAR)tcrop(b, (TINT32)0, (TINT32)255); + outPix->m = (UCHAR)tcrop(m, (TINT32)0, (TINT32)255); + } + } else { + *outPix = *downPix; + } + + FOR_EACH_PIXEL_32_END_LOOP + } else if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + double value0 = luminance(upPix); + double value1 = luminance(downPix); + + if (value0 > value1) { + TINT32 r, g, b, m; + if (upPix->m == 0) + *outPix = *downPix; + else { + r = downPix->r + upPix->r; + g = downPix->g + upPix->g; + b = downPix->b + upPix->b; + m = downPix->m + upPix->m; + outPix->r = (USHORT)tcrop(r, (TINT32)0, (TINT32)65535); + outPix->g = (USHORT)tcrop(g, (TINT32)0, (TINT32)65535); + outPix->b = (USHORT)tcrop(b, (TINT32)0, (TINT32)65535); + outPix->m = (USHORT)tcrop(m, (TINT32)0, (TINT32)65535); + } + } else { + *outPix = *downPix; + } + + FOR_EACH_PIXEL_64_END_LOOP + } else + throw TRopException("TRop::lighten invalid raster combination"); +} + +//----------------------------------------------------------------------------- + +void TRop::ropmin(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, bool matte) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + if (matte) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_32_END_LOOP + } else { + FOR_EACH_PIXEL_32_BEGIN_LOOP + if (upPix->m >= 255) { + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + } else if (upPix->m) { + TPixel32 tmp; + tmp.r = upPix->r < downPix->r ? upPix->r : downPix->r; + tmp.g = upPix->g < downPix->g ? upPix->g : downPix->g; + tmp.b = upPix->b < downPix->b ? upPix->b : downPix->b; + //tmp.m = upPix->m < downPix->m ? upPix->m : downPix->m; + outPix->r = upPix->m * (tmp.r - downPix->r) / 255.0 + downPix->r; + outPix->g = upPix->m * (tmp.g - downPix->g) / 255.0 + downPix->g; + outPix->b = upPix->m * (tmp.b - downPix->b) / 255.0 + downPix->b; + outPix->m = upPix->m * (tmp.m - downPix->m) / 255.0 + downPix->m; + } else + *outPix = *downPix; + FOR_EACH_PIXEL_32_END_LOOP + } + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + if (matte) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_64_END_LOOP + } else { + FOR_EACH_PIXEL_32_BEGIN_LOOP + if (upPix->m >= 65535) { + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + } else if (upPix->m) { + TPixel32 tmp; + tmp.r = upPix->r < downPix->r ? upPix->r : downPix->r; + tmp.g = upPix->g < downPix->g ? upPix->g : downPix->g; + tmp.b = upPix->b < downPix->b ? upPix->b : downPix->b; + //tmp.m = upPix->m < downPix->m ? upPix->m : downPix->m; + outPix->r = upPix->m * (tmp.r - downPix->r) / 65535.0 + downPix->r; + outPix->g = upPix->m * (tmp.g - downPix->g) / 65535.0 + downPix->g; + outPix->b = upPix->m * (tmp.b - downPix->b) / 65535.0 + downPix->b; + outPix->m = upPix->m * (tmp.m - downPix->m) / 65535.0 + downPix->m; + } else + *outPix = *downPix; + FOR_EACH_PIXEL_32_END_LOOP + } + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + outPix->value = upPix->value < downPix->value ? upPix->value : downPix->value; + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::min invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::ropmax(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + + outPix->r = upPix->r > downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g > downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b > downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + outPix->r = upPix->r > downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g > downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b > downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + outPix->value = upPix->value > downPix->value ? upPix->value : downPix->value; + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::max invalid raster combination"); + } + } +} +//----------------------------------------------------------------------------- + +void TRop::linearburn(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + if (upPix->m) { + TPixel32 app; + if (downPix->m) { + TINT32 r, g, b, m; + TPixel32 tmpPix; + tmpPix = depremultiply(*downPix); + r = tmpPix.r + upPix->r - 255; + g = tmpPix.g + upPix->g - 255; + b = tmpPix.b + upPix->b - 255; + m = tmpPix.m + upPix->m - 255; + + app.r = (UCHAR)tcrop(r, (TINT32)0, (TINT32)255); + app.g = (UCHAR)tcrop(g, (TINT32)0, (TINT32)255); + app.b = (UCHAR)tcrop(b, (TINT32)0, (TINT32)255); + //app.m = (UCHAR)tcrop (m, (TINT32)0, (TINT32)255); + app.m = upPix->m; + } else + app = *upPix; + overPix(*outPix, *downPix, app); + } + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + if (upPix->m) { + TPixel64 app; + if (downPix->m) { + TINT32 r, g, b, m; + TPixel64 tmpPix; + tmpPix = depremultiply(*downPix); + r = tmpPix.r + upPix->r - 65535; + g = tmpPix.g + upPix->g - 65535; + b = tmpPix.b + upPix->b - 65535; + m = tmpPix.m + upPix->m - 65535; + + app.r = (USHORT)tcrop(r, 0, 0xffff); + app.g = (USHORT)tcrop(g, 0, 0xffff); + app.b = (USHORT)tcrop(b, 0, 0xffff); + //app.m = (UCHAR)tcrop (m, (TINT32)0, (TINT32)255); + app.m = upPix->m; + } else + app = *upPix; + overPix(*outPix, *downPix, app); + } + + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + USHORT value = troundp(upPix->value + downPix->value - 255); + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::max invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::overlay(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) +{ + TRaster32P up32 = rup; + TRaster32P down32 = rdown; + TRaster32P out32 = rout; + + if (up32 && down32 && out32) { + FOR_EACH_PIXEL_32_BEGIN_LOOP + if (upPix->m) { + TPixel32 app; + TPixel32 tmpPix, tmp2Pix; + if (downPix->m) { + tmpPix = *downPix; + tmp2Pix = depremultiply(*upPix); + if (tmpPix.r < 128) + app.r = troundp(2 * tmp2Pix.r * (tmpPix.r / 255.0)); + else { + SHORT r = 255 * (1 - 2 * (1.0 - tmpPix.r / 255.0) * (1.0 - tmp2Pix.r / 255.0)); + app.r = (UCHAR)tcrop(r, 0, 255); + } + if (tmpPix.g < 128) + app.g = troundp(2 * tmp2Pix.g * (tmpPix.g / 255.0)); + else { + SHORT g = 255 * (1 - 2 * (1.0 - tmpPix.g / 255.0) * (1.0 - tmp2Pix.g / 255.0)); + app.g = (UCHAR)tcrop(g, 0, 255); + } + if (tmpPix.b < 128) + app.b = troundp(2 * tmp2Pix.b * (tmpPix.b / 255.0)); + else { + SHORT b = 255 * (1 - 2 * (1.0 - tmpPix.b / 255.0) * (1.0 - tmp2Pix.b / 255.0)); + app.b = (UCHAR)tcrop(b, 0, 255); + } + app.m = tmp2Pix.m; + app = premultiply(app); + } else + app = *upPix; + overPix(*outPix, *downPix, app); + } + FOR_EACH_PIXEL_32_END_LOOP + } else { + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + if (upPix->m) { + TPixel64 app; + TPixel64 tmpPix, tmp2Pix; + if (downPix->m) { + tmpPix = *downPix; + tmp2Pix = depremultiply(*upPix); + if (tmpPix.r < 32768) + app.r = troundp(2 * tmp2Pix.r * (tmpPix.r / 65535.0)); + else { + SHORT r = 65535 * (1 - 2 * (1.0 - tmpPix.r / 65535.0) * (1.0 - tmp2Pix.r / 65535.0)); + app.r = (USHORT)tcrop(r, 0, 65535); + } + if (tmpPix.g < 32768) + app.g = troundp(2 * tmp2Pix.g * (tmpPix.g / 65535.0)); + else { + SHORT g = 65535 * (1 - 2 * (1.0 - tmpPix.g / 65535.0) * (1.0 - tmp2Pix.g / 65535.0)); + app.g = (USHORT)tcrop(g, 0, 65535); + } + if (tmpPix.b < 32768) + app.b = troundp(2 * tmp2Pix.b * (tmpPix.b / 65535.0)); + else { + SHORT b = 65535 * (1 - 2 * (1.0 - tmpPix.b / 65535.0) * (1.0 - tmp2Pix.b / 65535.0)); + app.b = (USHORT)tcrop(b, 0, 65535); + } + app.m = tmp2Pix.m; + app = premultiply(app); + } else + app = *upPix; + overPix(*outPix, *downPix, app); + } + FOR_EACH_PIXEL_64_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + USHORT value; + if (downPix->value < 128) + value = troundp(2 * upPix->value * (downPix->value / 255.0)); + else + value = 255 * (1 - 2 * (1.0 - downPix->value / 255.0) * (1.0 - upPix->value / 255.0)); + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::max invalid raster combination"); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::premultiply(const TRasterP &ras) +{ + ras->lock(); + TRaster32P ras32 = ras; + if (ras32) { + TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); + TPixel32 *lastPix = upRow + ras32->getWrap() * (ras32->getLy() - 1) + ras32->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras32->getLx(); + while (upPix < endPix) { + premult(*upPix); + ++upPix; + } + upRow += ras32->getWrap(); + } + } else { + TRaster64P ras64 = ras; + if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + premult(*upPix); + ++upPix; + } + upRow += ras64->getWrap(); + } + } else { + ras->unlock(); + throw TException("TRop::premultiply invalid raster type"); + } + } + ras->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::depremultiply(const TRasterP &ras) +{ + ras->lock(); + TRaster32P ras32 = ras; + if (ras32) { + TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); + TPixel32 *lastPix = upRow + ras32->getWrap() * (ras32->getLy() - 1) + ras32->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras32->getLx(); + while (upPix < endPix) { + depremult(*upPix); + ++upPix; + } + upRow += ras32->getWrap(); + } + } else { + TRaster64P ras64 = ras; + if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + depremult(*upPix); + ++upPix; + } + upRow += ras64->getWrap(); + } + } else { + ras->unlock(); + throw TException("TRop::depremultiply invalid raster type"); + } + } + ras->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::whiteTransp(const TRasterP &ras) +{ + ras->lock(); + TRaster32P ras32 = ras; + if (ras32) { + TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); + TPixel32 *lastPix = upRow + ras32->getWrap() * (ras32->getLy() - 1) + ras32->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras32->getLx(); + while (upPix < endPix) { + if (*upPix == TPixel::White) + *upPix = TPixel::Transparent; + ++upPix; + } + upRow += ras32->getWrap(); + } + } else { + TRaster64P ras64 = ras; + if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + if (*upPix == TPixel64::White) + *upPix = TPixel64::Transparent; + ++upPix; + } + upRow += ras64->getWrap(); + } + } else { + ras->unlock(); + throw TException("TRop::premultiply invalid raster type"); + } + } + ras->unlock(); +} + +//----------------------------------------------------------------------------- + +template +const double *premultiplyTable() +{ + static double *table = 0; + if (!table) { + int maxChannelValue = (std::numeric_limits::max)(); + int chanValuesCount = maxChannelValue + 1; + double maxD = maxChannelValue; + + table = new double[chanValuesCount]; + + for (int i = 0; i < chanValuesCount; ++i) + table[i] = i / maxD; + } + + return table; +} + +template DVAPI const double *premultiplyTable(); +template DVAPI const double *premultiplyTable(); + +//----------------------------------------------------------------------------- + +template +const double *depremultiplyTable() +{ + static double *table = 0; + if (!table) { + int maxChannelValue = (std::numeric_limits::max)(); + int chanValuesCount = maxChannelValue + 1; + double maxD = maxChannelValue; + + table = new double[chanValuesCount]; + + table[0] = 0.0; + for (int i = 1; i < chanValuesCount; ++i) + table[i] = maxD / i; + } + + return table; +} + +template DVAPI const double *depremultiplyTable(); +template DVAPI const double *depremultiplyTable(); + +//----------------------------------------------------------------------------- + +#undef FOR_EACH_PIXEL_BEGIN_LOOP +#undef FOR_EACH_PIXEL_32_BEGIN_LOOP +#undef FOR_EACH_PIXEL_64_BEGIN_LOOP +#undef FOR_EACH_PIXEL_8_BEGIN_LOOP +#undef FOR_EACH_PIXEL_END_LOOP +#undef FOR_EACH_PIXEL_32_END_LOOP +#undef FOR_EACH_PIXEL_64_END_LOOP +#undef FOR_EACH_PIXEL_8_END_LOOP diff --git a/toonz/sources/common/trop/tover.cpp b/toonz/sources/common/trop/tover.cpp new file mode 100644 index 0000000..7346382 --- /dev/null +++ b/toonz/sources/common/trop/tover.cpp @@ -0,0 +1,514 @@ + + +#include "quickputP.h" +#include "tpixelutils.h" +#include "trastercm.h" +#include "tsystem.h" +#include "tropcm.h" +#include "tpalette.h" + +#ifdef WIN32 +#include // per SSE2 +#endif + +//----------------------------------------------------------------------------- +namespace +{ + +inline bool transp(const TPixel32 &p) +{ + return p.m == 0; +} +inline bool transp(const TPixel64 &p) +{ + return p.m == 0; +} + +//----------------------------------------------------------------------------- + +inline bool opaque(const TPixel32 &p) +{ + return p.m == 0xff; +} +inline bool opaque(const TPixel64 &p) +{ + return p.m == 0xffff; +} + +//----------------------------------------------------------------------------- + +#define MODO2 +#define VELOCE +template +void do_overT3(TRasterPT rout, const TRasterPT &rdn, const TRasterPT &rup) +{ + + for (int y = 0; y < rout->getLy(); y++) { +#ifdef MODO1 + const T *dn_pix = rdn->pixels(y); + const T *up_pix = rup->pixels(y); + T *out_pix = rout->pixels(y); + +#else +#ifdef MODO2 + const T *dn_pix = ((T *)rdn->getRawData()) + y * rdn->getWrap(); + const T *up_pix = ((T *)rup->getRawData()) + y * rup->getWrap(); + + T *out_pix = ((T *)rout->getRawData()) + y * rout->getWrap(); +#endif +#endif + + const T *dn_limit = dn_pix + rdn->getLx(); + for (; dn_pix < dn_limit; dn_pix++, up_pix++, out_pix++) { +#ifdef VELOCE + if (transp(*up_pix)) + *out_pix = *dn_pix; + else if (opaque(*up_pix)) + *out_pix = *up_pix; + else { + *out_pix = overPix(*dn_pix, *up_pix); + } +#else + + T topval = *up_pix; + if (transp(topval)) + *out_pix = *dn_pix; + else if (opaque(topval)) + *out_pix = topval; + else { + *out_pix = overPix(*dn_pix, topval); + } +#endif + } + } +} + +//----------------------------------------------------------------------------- + +template +void do_over(TRasterPT rout, const TRasterPT &rdn, + const TRasterPT &rup, const TRasterGR8P rmask) +{ + + for (int y = 0; y < rout->getLy(); y++) { + const PixTypeDn *dn_pix = ((PixTypeDn *)rdn->getRawData()) + y * rdn->getWrap(); + const PixTypeUp *up_pix = ((PixTypeUp *)rup->getRawData()) + y * rup->getWrap(); + + PixTypeOut *out_pix = ((PixTypeOut *)rout->getRawData()) + y * rout->getWrap(); + TPixelGR8 *mask_pix = ((TPixelGR8 *)rmask->getRawData()) + y * rmask->getWrap(); + + const PixTypeDn *dn_limit = dn_pix + rout->getLx(); + for (; dn_pix < dn_limit; dn_pix++, up_pix++, out_pix++, mask_pix++) { + if (mask_pix->value == 0x00) + *out_pix = *dn_pix; + else if (mask_pix->value == 0xff) + *out_pix = *up_pix; + else { + PixTypeUp p(*up_pix); + p.m = mask_pix->value; + *out_pix = overPix(*dn_pix, p); //hei! + } + } + } +} + +//----------------------------------------------------------------------------- + +void do_over(TRasterCM32P rout, const TRasterCM32P &rup) +{ + assert(rout->getSize() == rup->getSize()); + for (int y = 0; y < rout->getLy(); y++) { + TPixelCM32 *out_pix = rout->pixels(y); + TPixelCM32 *const out_end = out_pix + rout->getLx(); + const TPixelCM32 *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + if (!up_pix->isPureInk() && + up_pix->getPaint() != 0) // BackgroundStyle) + *out_pix = *up_pix; + else if (!up_pix->isPurePaint()) { + TUINT32 *outl = (TUINT32 *)out_pix, *upl = (TUINT32 *)up_pix; + + *outl = ((*upl) & (TPixelCM32::getInkMask())) | + ((*outl) & (TPixelCM32::getPaintMask())) | + tmin(up_pix->getTone(), out_pix->getTone()); + } + } + } +} + +//----------------------------------------------------------------------------- + +template +void do_overT2(TRasterPT rout, const TRasterPT &rup) +{ + UINT max = T::maxChannelValue; + double maxD = max; + + assert(rout->getSize() == rup->getSize()); + for (int y = 0; y < rout->getLy(); y++) { + T *out_pix = rout->pixels(y); + T *const out_end = out_pix + rout->getLx(); + const T *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + if (up_pix->m == max) + *out_pix = *up_pix; + else if (up_pix->m > 0) { + TUINT32 r, g, b; + r = up_pix->r + (out_pix->r * (max - up_pix->m)) / maxD; + g = up_pix->g + (out_pix->g * (max - up_pix->m)) / maxD; + b = up_pix->b + (out_pix->b * (max - up_pix->m)) / maxD; + + out_pix->r = (r < max) ? (Q)r : (Q)max; + out_pix->g = (g < max) ? (Q)g : (Q)max; + out_pix->b = (b < max) ? (Q)b : (Q)max; + out_pix->m = up_pix->m + (out_pix->m * (max - up_pix->m)) / maxD; + } + } + } +} + +//----------------------------------------------------------------------------- + +#ifdef WIN32 + +void do_over_SSE2(TRaster32P rout, const TRaster32P &rup) +{ + __m128i zeros = _mm_setzero_si128(); + __m128i out_pix_packed_i, up_pix_packed_i; + __m128 out_pix_packed, up_pix_packed; + + float maxChannelValue = 255.0; + float maxChannelValueInv = 1.0f / maxChannelValue; + + __m128 maxChanneValue_packed = _mm_load1_ps(&maxChannelValue); + + assert(rout->getSize() == rup->getSize()); + for (int y = 0; y < rout->getLy(); y++) { + TPixel32 *out_pix = rout->pixels(y); + TPixel32 *const out_end = out_pix + rout->getLx(); + const TPixel32 *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + if (up_pix->m == 0xff) + *out_pix = *up_pix; + else if (up_pix->m > 0) { + float factor = (255.0f - up_pix->m) / 255.0f; + __m128 factor_packed = _mm_load1_ps(&factor); + + // carica up_pix e out_pix in due registri a 128 bit + up_pix_packed_i = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)up_pix), zeros); + up_pix_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(up_pix_packed_i, zeros)); + + out_pix_packed_i = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)out_pix), zeros); + out_pix_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(out_pix_packed_i, zeros)); + + out_pix_packed = _mm_add_ps(up_pix_packed, _mm_mul_ps(out_pix_packed, factor_packed)); + out_pix_packed = _mm_min_ps(maxChanneValue_packed, out_pix_packed); + + out_pix_packed_i = _mm_cvtps_epi32(out_pix_packed); + out_pix_packed_i = _mm_packs_epi32(out_pix_packed_i, zeros); + out_pix_packed_i = _mm_packus_epi16(out_pix_packed_i, zeros); + *(DWORD *)(out_pix) = _mm_cvtsi128_si32(out_pix_packed_i); + } + } + } +} + +#endif + +//----------------------------------------------------------------------------- + +void do_over(TRaster32P rout, const TRasterGR8P &rup) +{ + assert(rout->getSize() == rup->getSize()); + for (int y = rout->getLy(); --y >= 0;) { + TPixel32 *out_pix = rout->pixels(y); + TPixel32 *const out_end = out_pix + rout->getLx(); + const TPixelGR8 *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + int v = up_pix->value; + out_pix->r = out_pix->r * v / 255; + out_pix->g = out_pix->r; + out_pix->b = out_pix->r; + } + } +} + +//----------------------------------------------------------------------------- + +void do_over(TRasterGR8P rout, const TRaster32P &rup) +{ + assert(rout->getSize() == rup->getSize()); + for (int y = rout->getLy(); --y >= 0;) { + TPixelGR8 *out_pix = rout->pixels(y); + TPixelGR8 *const out_end = out_pix + rout->getLx(); + const TPixel32 *up_pix = rup->pixels(y); + TPixel32 *temp_pix = new TPixel32(); + for (; out_pix < out_end; ++out_pix, ++up_pix) { + temp_pix->r = out_pix->value; + temp_pix->g = out_pix->value; + temp_pix->b = out_pix->value; + temp_pix->m = 0xff; + TPixel32 out32_pix = overPix(*temp_pix, *up_pix); + *out_pix = out_pix->from(out32_pix); + } + } +} + +} // namespace + +//----------------------------------------------------------------------------- + +void do_over(TRaster32P rout, const TRasterGR8P &rup, const TPixel32 &color) +{ + assert(rout->getSize() == rup->getSize()); + for (int y = rout->getLy(); --y >= 0;) { + TPixel32 *out_pix = rout->pixels(y); + TPixel32 *const out_end = out_pix + rout->getLx(); + const TPixelGR8 *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + double v = up_pix->value / 255.0; + TPixel32 up(troundp(v * color.r), troundp(v * color.g), troundp(v * color.b), troundp(v * color.m)); + *out_pix = overPix(*out_pix, up); + } + } +} + +//----------------------------------------------------------------------------- + +void TRop::over(TRaster32P rout, const TRasterGR8P &rup, const TPixel32 &color) +{ + rout->lock(); + do_over(rout, rup, color); + rout->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::over(const TRasterP &rout, const TRasterP &rdn, const TRasterP &rup) +{ + TRect rect = rout->getBounds() * rdn->getBounds() * rup->getBounds(); + if (rect.isEmpty()) + return; + + TRasterP cRout = rout->extract(rect); + TRasterP cRdn = rdn->extract(rect); + TRasterP cRup = rup->extract(rect); + rout->lock(); + rdn->lock(); + rup->lock(); + TRaster32P rout32 = cRout, rdn32 = cRdn, rup32 = cRup; + TRaster64P rout64 = cRout, rdn64 = cRdn, rup64 = cRup; + if (rout32 && rdn32 && rup32) + do_overT3(rout32, rdn32, rup32); + else if (rout64 && rdn64 && rup64) + do_overT3(rout64, rdn64, rup64); + else { + rout->unlock(); + rdn->unlock(); + rup->unlock(); + throw TRopException("unsupported pixel type"); + } + + rout->unlock(); + rdn->unlock(); + rup->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::over(const TRasterP &rout, const TRasterP &rup, const TPoint &pos) +{ + TRect outRect(rout->getBounds()); + TRect upRect(rup->getBounds() + pos); + TRect intersection = outRect * upRect; + if (intersection.isEmpty()) + return; + + TRasterP cRout = rout->extract(intersection); + TRect r = intersection - pos; + TRasterP cRup = rup->extract(r); + + TRaster32P rout32 = cRout, rup32 = cRup; + TRaster64P rout64 = cRout, rup64 = cRup; + + TRasterGR8P rout8 = cRout, rup8 = cRup; + + TRasterCM32P routCM32 = cRout, rupCM32 = cRup; + + rout->lock(); + rup->lock(); + + // TRaster64P rout64 = rout, rin64 = rin; + if (rout32 && rup32) { +#ifdef WIN32 + if (TSystem::getCPUExtensions() & TSystem::CpuSupportsSse2) + do_over_SSE2(rout32, rup32); + else +#endif + do_overT2(rout32, rup32); + } else if (rout64) { + if (!rup64) { + TRaster64P raux(cRup->getSize()); + TRop::convert(raux, cRup); + rup64 = raux; + } + do_overT2(rout64, rup64); + } else if (rout32 && rup8) + do_over(rout32, rup8); + else if (rout8 && rup32) + do_over(rout8, rup32); + else if (rout8 && rup8) + TRop::copy(rout8, rup8); + else if (routCM32 && rupCM32) + do_over(routCM32, rupCM32); + else { + rout->unlock(); + rup->unlock(); + throw TRopException("unsupported pixel type"); + } + + rout->unlock(); + rup->unlock(); +} + +//----------------------------------------------------------------------------- + +static void addBackground32(TRaster32P ras, const TPixel32 &col) +{ + ras->lock(); + int nrows = ras->getLy(); + while (nrows-- > 0) { + TPixel32 *pix = ras->pixels(nrows); + TPixel32 *endPix = pix + ras->getLx(); + while (pix < endPix) { + *pix = overPix(col, *pix); + pix++; + } + } + ras->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::addBackground(TRasterP ras, const TPixel32 &col) +{ + TRaster32P ras32 = ras; + if (ras32) + addBackground32(ras32, col); + else + throw TRopException("unsupported pixel type"); +} + +//=================================================================== + +//Usata tinylinetest +static void my_do_over(TRaster32P rout, const TRasterGR8P &rup) +{ + assert(rout->getSize() == rup->getSize()); + for (int y = rout->getLy(); --y >= 0;) { + TPixel32 *out_pix = rout->pixels(y); + TPixel32 *const out_end = out_pix + rout->getLx(); + const TPixelGR8 *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + int v = up_pix->value; + out_pix->r = out_pix->r * v / 255; + out_pix->g = out_pix->r; + out_pix->b = out_pix->r; + } + } +} + +//=================================================================== + +void TRop::over(const TRasterP &out, + const TRasterP &up, + const TAffine &aff, + ResampleFilterType filterType) +{ + out->lock(); + up->lock(); + + if (filterType == ClosestPixel || filterType == Bilinear) + ::quickPut(out, up, aff, filterType); + else { + TRect rasterBounds = up->getBounds(); + TRectD dbounds(rasterBounds.x0, rasterBounds.y0, rasterBounds.x1 + 1, rasterBounds.y1 + 1); + dbounds = aff * dbounds; + TRect bounds(tfloor(dbounds.x0), tfloor(dbounds.y0), tceil(dbounds.x1) - 1, tceil(dbounds.y1) - 1); + TRasterP tmp = up->create(bounds.getLx(), bounds.getLy()); + resample(tmp, up, TTranslation(-bounds.x0, -bounds.y0) * aff, filterType); + over(out, tmp, bounds.getP00()); + } + out->unlock(); + up->unlock(); +} + +void TRop::over(const TRasterP &out, const TRasterP &up, const TPoint &pos, const TAffine &aff, + ResampleFilterType filterType) +{ + if (aff.isIdentity()) + //simple over with offset + TRop::over(out, up, pos); + else { + TRect rasterBounds = up->getBounds(); + TRectD dbounds(rasterBounds.x0, rasterBounds.y0, rasterBounds.x1, rasterBounds.y1); + dbounds = aff * dbounds; + TRect bounds(tfloor(dbounds.x0), tfloor(dbounds.y0), tceil(dbounds.x1), tceil(dbounds.y1)); + TRasterP tmp = up->create(bounds.getLx(), bounds.getLy()); + resample(tmp, up, TTranslation(-dbounds.getP00()) * aff, filterType); + TRop::over(out, tmp, pos); + } +} + +void TRop::over(TRasterP rout, const TRasterCM32P &rup, TPalette *palette, const TPoint &point, const TAffine &aff) +{ + TRaster32P app(rup->getSize()); + TRop::convert(app, rup, palette); + TRop::over(rout, app, point, aff); +} + +//=================================================================== + +void TRop::quickPut(const TRasterP &out, + const TRasterP &up, + const TAffine &aff, + const TPixel32 &colorScale, + bool doPremultiply, bool whiteTransp, bool firstColumn, + bool doRasterDarkenBlendedView) +{ + ::quickPut(out, up, aff, ClosestPixel, colorScale, doPremultiply, whiteTransp, firstColumn, doRasterDarkenBlendedView); +} + +//=================================================================== + +void TRop::over(const TRasterP &out, const TRasterP &dn, const TRasterP &up, const TRasterGR8P &mask) +{ + out->lock(); + up->lock(); + dn->lock(); + + TRaster32P out32 = out; + TRaster32P dn32 = dn; + TRaster32P up32 = up; + + if (out32 && dn32 && up32) + do_over(out32, dn32, up32, mask); + else { + TRaster64P out64 = out; + TRaster64P dn64 = dn; + TRaster64P up64 = up; + if (out64 && dn64 && up64) + do_over(out64, dn64, up64, mask); + else + throw TRopException("unsupported pixel type"); + } + out->unlock(); + up->unlock(); + dn->unlock(); +} diff --git a/toonz/sources/common/trop/traylit.cpp b/toonz/sources/common/trop/traylit.cpp new file mode 100644 index 0000000..58d5701 --- /dev/null +++ b/toonz/sources/common/trop/traylit.cpp @@ -0,0 +1,408 @@ + + +#include "traster.h" +#include "tpixelutils.h" + +#include "trop.h" + +//*********************************************************************************************** +// Local namespace stuff +//*********************************************************************************************** + +namespace +{ + +template +struct RaylitFuncTraits { + typedef void (*function_type)(T *, T *, int, int, int, int, const TRect &, const TRect &, const TRop::RaylitParams &); +}; + +//-------------------------------------------------------------------------------------------- + +template +void performStandardRaylit(T *bufIn, T *bufOut, + int dxIn, int dyIn, int dxOut, int dyOut, + const TRect &srcRect, const TRect &dstRect, + const TRop::RaylitParams ¶ms) +{ + /* NOTATION: Diagram assuming octant 1 + + / | + / | + / - ray_final_y | octLy + / 1 | + +---- | + _____ octLx + + + So, octLx and octLy are the octant's lx and ly; ray_final_y is the final height of the ray we're tracing + */ + + // Build colors-related variables + int max = T::maxChannelValue; + /*-- 透明部分の色 --*/ + int transp_val = (params.m_invert) ? max : 0, opaque_val = max - transp_val; + int value, val_r, val_g, val_b, val_m; + double lightness, r_fac, g_fac, b_fac, m_fac; + /*-- 8bit/16bitの違いを吸収する係数 --*/ + double factor = max / 255.0; + + double scale = params.m_scale; // NOTE: These variable initializations are, well, + double decay = log(params.m_decay / 100.0 + 1.0) + 1.0; // heuristic at least. They were probably tested + double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale; // to be good, but didn't quite make any REAL sense. + double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); // + // They could be done MUCH better, but changing them + /*-- 1ステップ進んだ時、次のピクセルで光源が無かったときの光の弱まる割合 --*/ + double neg_delta_p = smoothness * intensity; // would alter the way raylit has been applied until now. + /*-- 1ステップ進んだ時、次のピクセルで光源が有ったときの光の強まる割合 --*/ + double quot_delta_p = intensity / max; // + // Should be changed at some point, though... + /*-- m_colorはRaylitFxのColor値。r_fac、g_fac、b_facは各チャンネルをPremultiplyした値 --*/ + m_fac = (params.m_color.m / 255.0); + r_fac = m_fac * (params.m_color.r / 255.0); + g_fac = m_fac * (params.m_color.g / 255.0); + b_fac = m_fac * (params.m_color.b / 255.0); + + // Geometry-related variables + int x, y, ray_final_y; + int octLx = dstRect.x1 - dstRect.x0; + + double rayPosIncrementX = 1.0 / scale; + + double sq_z = sq(params.m_lightOriginSrc.z); // We'll be making square distances from p, so square it once now + + // Perform raylit + T *pixIn, *pixOut; + + for (ray_final_y = 0; ray_final_y < octLx; ++ray_final_y) { + // Initialize increment variables + lightness = 0.0; + + double rayPosIncrementY = rayPosIncrementX * (ray_final_y / (double)octLx); + + // Use an integer counter to know when y must increase. Will add ray_final_y as long as + // a multiple of octLx-1 is reached, then increase + int yIncrementCounter = 0, yIncrementThreshold = octLx - 1; + + // Trace a single ray of light + TPointD rayPos(rayPosIncrementX, rayPosIncrementY); + + for (x = dstRect.x0, y = dstRect.y0, pixIn = bufIn, pixOut = bufOut; (x < dstRect.x1) && (y < dstRect.y1); ++x) { + bool insideSrc = (x >= srcRect.x0) && (x < srcRect.x1) && (y >= srcRect.y0) && (y < srcRect.y1); + if (insideSrc) { + // Add a light component depending on source's matte + if (pixIn->m == opaque_val) + lightness = tmax(0.0, lightness - neg_delta_p); // No light source - ray fading + else { + if (pixIn->m == transp_val) + lightness += intensity; // Full light source - ray enforcing + else + lightness = tmax(0.0, lightness + // Half light source + (params.m_invert ? pixIn->m : (max - pixIn->m)) * quot_delta_p); // matte-linear enforcing + } + + if (params.m_includeInput) { + val_r = pixIn->r; + val_g = pixIn->g; + val_b = pixIn->b; + val_m = pixIn->m; + } else + val_r = val_g = val_b = val_m = 0; + } else { + if (!params.m_invert) + lightness += intensity; + else + lightness = tmax(0.0, lightness - neg_delta_p); + + val_r = val_g = val_b = val_m = 0; + } + + bool insideDst = (x >= 0) && (y >= 0); + if (insideDst) { + // Write the corresponding destination pixel + if (lightness > 0.0) + value = (int)(factor * lightness / (rayPos.x * pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), decay)) + 0.5); // * ^-d... 0.5 rounds + else + value = 0; + + //NOTE: pow() could be slow. If that is the case, it could be cached for the whole octant along the longest ray at integer positions, + // and then linearly interpolated between those... Have to profile this before resorting to that... + + val_r += value * r_fac; + val_g += value * g_fac; + val_b += value * b_fac; + val_m += value * m_fac; + + pixOut->r = (val_r > max) ? max : val_r; + pixOut->g = (val_g > max) ? max : val_g; + pixOut->b = (val_b > max) ? max : val_b; + pixOut->m = (val_m > max) ? max : val_m; + } + + // Increment variables along the x-axis + pixIn += dxIn, pixOut += dxOut; + + rayPos.x += rayPosIncrementX, rayPos.y += rayPosIncrementY; + + // Increment variables along the y-axis + if ((yIncrementCounter += ray_final_y) >= yIncrementThreshold) { + ++y, pixIn += dyIn, pixOut += dyOut; + yIncrementCounter -= yIncrementThreshold; + } + } + } +} + +//-------------------------------------------------------------------------------------------- + +template +void performColorRaylit(T *bufIn, T *bufOut, + int dxIn, int dyIn, int dxOut, int dyOut, + const TRect &srcRect, const TRect &dstRect, + const TRop::RaylitParams ¶ms) +{ + // Build colors-related variables + int max = T::maxChannelValue; + + int val_r, val_g, val_b, val_m; + double lightness_r, lightness_g, lightness_b; + double factor = max / 255.0; + + double scale = params.m_scale; // NOTE: These variable initializations are, well, + double decay = log(params.m_decay / 100.0 + 1.0) + 1.0; // heuristic at least. They were probably tested + double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale; // to be good, but didn't quite make any REAL sense. + double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); // + // They could be done MUCH better, but changing them + double neg_delta_p = smoothness * intensity; // would alter the way raylit has been applied until now. + double quot_delta_p = intensity / max; // + // Should be changed at some point, though... + + // Geometry-related variables + int x, y, ray_final_y; + int octLx = dstRect.x1 - dstRect.x0; + + double rayPosIncrementX = 1.0 / scale; + + double fac, sq_z = sq(params.m_lightOriginSrc.z); // We'll be making square distances from p, so square it once now + + // Perform raylit + T *pixIn, *pixOut; + + for (ray_final_y = 0; ray_final_y < octLx; ++ray_final_y) { + // Initialize increment variables + lightness_r = lightness_g = lightness_b = 0.0; + int l, l_max; + + double rayPosIncrementY = rayPosIncrementX * (ray_final_y / (double)octLx); + + // Use an integer counter to know when y must increase. Will add ray_final_y as long as + // a multiple of octLx-1 is reached, then increase + int yIncrementCounter = 0, yIncrementThreshold = octLx - 1; + + // Trace a single ray of light + TPointD rayPos(rayPosIncrementX, rayPosIncrementY); + + for (x = dstRect.x0, y = dstRect.y0, pixIn = bufIn, pixOut = bufOut; (x < dstRect.x1) && (y < dstRect.y1); ++x) { + bool insideSrc = (x >= srcRect.x0) && (x < srcRect.x1) && (y >= srcRect.y0) && (y < srcRect.y1); + if (insideSrc) { + val_r = pixIn->r; + val_g = pixIn->g; + val_b = pixIn->b; + val_m = pixIn->m; + + lightness_r = tmax(0.0, val_r ? lightness_r + val_r * quot_delta_p : lightness_r - neg_delta_p); + lightness_g = tmax(0.0, val_g ? lightness_g + val_g * quot_delta_p : lightness_g - neg_delta_p); + lightness_b = tmax(0.0, val_b ? lightness_b + val_b * quot_delta_p : lightness_b - neg_delta_p); + + if (!params.m_includeInput) + val_r = val_g = val_b = val_m = 0; + } else { + lightness_r = tmax(0.0, lightness_r - neg_delta_p); + lightness_g = tmax(0.0, lightness_g - neg_delta_p); + lightness_b = tmax(0.0, lightness_b - neg_delta_p); + + val_r = val_g = val_b = val_m = 0; + } + + bool insideDst = (x >= 0) && (y >= 0); + if (insideDst) { + // Write the corresponding destination pixel + fac = factor / (rayPos.x * pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), decay)); + + //NOTE: pow() could be slow. If that is the case, it could be cached for the whole octant along the longest ray at integer positions, + // and then linearly interpolated between those... Have to profile this before resorting to that... + + val_r += l = (int)(fac * lightness_r + 0.5); + l_max = l; + val_g += l = (int)(fac * lightness_g + 0.5); + l_max = tmax(l, l_max); + val_b += l = (int)(fac * lightness_b + 0.5); + l_max = tmax(l, l_max); + val_m += l_max; + + pixOut->r = (val_r > max) ? max : val_r; + pixOut->g = (val_g > max) ? max : val_g; + pixOut->b = (val_b > max) ? max : val_b; + pixOut->m = (val_m > max) ? max : val_m; + } + + // Increment variables along the x-axis + pixIn += dxIn, pixOut += dxOut; + + rayPos.x += rayPosIncrementX, rayPos.y += rayPosIncrementY; + + // Increment variables along the y-axis + if ((yIncrementCounter += ray_final_y) >= yIncrementThreshold) { + ++y, pixIn += dyIn, pixOut += dyOut; + yIncrementCounter -= yIncrementThreshold; + } + } + } +} + +//-------------------------------------------------------------------------------------------- +/*-- ピザ状に8分割された領域の1つを計算する --*/ +template +void computeOctant(const TRasterPT &src, const TRasterPT &dst, + int octant, const TRop::RaylitParams ¶ms, + typename RaylitFuncTraits::function_type raylitFunc) +{ + // Build octant geometry variables + int x0, x1, lxIn, lxOut, dxIn, dxOut; + int y0, y1, lyIn, lyOut, dyIn, dyOut; + + const T3DPoint &pIn = params.m_lightOriginSrc, &pOut = params.m_lightOriginDst; + int srcWrap = src->getWrap(), dstWrap = dst->getWrap(); + + T *bufIn = (T *)src->getRawData() + tfloor(pIn.y) * srcWrap + tfloor(pIn.x); + T *bufOut = (T *)dst->getRawData() + tfloor(pOut.y) * dstWrap + tfloor(pOut.x); + + TRect srcRect(src->getBounds() + TPoint(tround(pOut.x - pIn.x), tround(pOut.y - pIn.y))); + + lxIn = src->getLx(), lxOut = dst->getLx(); + lyIn = src->getLy(), lyOut = dst->getLy(); + + /*-- 1ピクセルずつ進むときの移動値 --*/ + // Vertical octant pairs + if (octant == 1 || octant == 8) + dxIn = 1, dxOut = 1, x0 = tfloor(pOut.x), x1 = lxOut; + if (octant == 2 || octant == 7) + dyIn = 1, dyOut = 1, y0 = tfloor(pOut.x), y1 = lxOut; + if (octant == 3 || octant == 6) + dyIn = -1, dyOut = -1, y0 = lxOut - tfloor(pOut.x) - 1, y1 = lxOut, tswap(srcRect.x0, srcRect.x1), srcRect.x0 = lxOut - srcRect.x0, srcRect.x1 = lxOut - srcRect.x1; + if (octant == 4 || octant == 5) + dxIn = -1, dxOut = -1, x0 = lxOut - tfloor(pOut.x) - 1, x1 = lxOut, tswap(srcRect.x0, srcRect.x1), srcRect.x0 = lxOut - srcRect.x0, srcRect.x1 = lxOut - srcRect.x1; + + // Horizontal octant pairs + if (octant == 2 || octant == 3) + dxIn = srcWrap, dxOut = dstWrap, x0 = tfloor(pOut.y), x1 = lyOut; + if (octant == 1 || octant == 4) + dyIn = srcWrap, dyOut = dstWrap, y0 = tfloor(pOut.y), y1 = lyOut; + if (octant == 5 || octant == 8) + dyIn = -srcWrap, dyOut = -dstWrap, y0 = lyOut - tfloor(pOut.y) - 1, y1 = lyOut, tswap(srcRect.y0, srcRect.y1), srcRect.y0 = lyOut - srcRect.y0, srcRect.y1 = lyOut - srcRect.y1; + if (octant == 6 || octant == 7) + dxIn = -srcWrap, dxOut = -dstWrap, x0 = lyOut - tfloor(pOut.y) - 1, x1 = lyOut, tswap(srcRect.y0, srcRect.y1), srcRect.y0 = lyOut - srcRect.y0, srcRect.y1 = lyOut - srcRect.y1; + + /*-- 縦向きのピザ領域を計算する場合は、90度回転してから --*/ + // Swap x and y axis where necessary + if (octant == 2 || octant == 3 || octant == 6 || octant == 7) { + tswap(lxIn, lyIn), tswap(lxOut, lyOut); + tswap(srcRect.x0, srcRect.y0), tswap(srcRect.x1, srcRect.y1); + } + + int octLx = (x1 - x0), octLy = (y1 - y0); + + assert(octLx > 0 && octLy > 0); + if (octLx <= 0 && octLy <= 0) + return; + + raylitFunc( + bufIn, bufOut, + dxIn, dyIn, dxOut, dyOut, + srcRect, TRect(x0, y0, x1, y1), + params); +} + +//-------------------------------------------------------------------------------------------- + +/* + OCTANTS: + + \ 3 | 2 / + \ | / + \ | / + 4 \|/ 1 + ----+---- + 5 /|\ 8 + / | \ + / | \ + / 6 | 7 \ +*/ + +template +void doRaylit(const TRasterPT &src, const TRasterPT &dst, const TRop::RaylitParams ¶ms, + typename RaylitFuncTraits::function_type raylitFunc) +{ + int lxOut = dst->getLx(), lyOut = dst->getLy(); + const T3DPoint &p = params.m_lightOriginDst; + + src->lock(); + dst->lock(); + + // Depending on the position of p, only some of the quadrants need to be built + if (p.y < lyOut) { + if (p.x < lxOut) { + // Compute the raylit fx on each octant independently + computeOctant(src, dst, 1, params, raylitFunc); + computeOctant(src, dst, 2, params, raylitFunc); + } + + if (p.x >= 0) { + computeOctant(src, dst, 3, params, raylitFunc); + computeOctant(src, dst, 4, params, raylitFunc); + } + } + + if (p.y >= 0) { + if (p.x >= 0) { + computeOctant(src, dst, 5, params, raylitFunc); + computeOctant(src, dst, 6, params, raylitFunc); + } + + if (p.x < lxOut) { + computeOctant(src, dst, 7, params, raylitFunc); + computeOctant(src, dst, 8, params, raylitFunc); + } + } + + dst->unlock(); + src->unlock(); +} + +} // namespace + +//*********************************************************************************************** +// TRop::raylit implementation +//*********************************************************************************************** + +void TRop::raylit(const TRasterP &dstRas, const TRasterP &srcRas, const RaylitParams ¶ms) +{ + if ((TRaster32P)dstRas && (TRaster32P)srcRas) + doRaylit(srcRas, dstRas, params, &performStandardRaylit); + else if ((TRaster64P)dstRas && (TRaster64P)srcRas) + doRaylit(srcRas, dstRas, params, &performStandardRaylit); + else + throw TException("TRop::raylit unsupported pixel type"); +} + +//-------------------------------------------------------------------------------------------- + +void TRop::glassRaylit(const TRasterP &dstRas, const TRasterP &srcRas, const RaylitParams ¶ms) +{ + if ((TRaster32P)dstRas && (TRaster32P)srcRas) + doRaylit(srcRas, dstRas, params, &performColorRaylit); + else if ((TRaster64P)dstRas && (TRaster64P)srcRas) + doRaylit(srcRas, dstRas, params, &performColorRaylit); + else + throw TException("TRop::raylit unsupported pixel type"); +} diff --git a/toonz/sources/common/trop/tresample.cpp b/toonz/sources/common/trop/tresample.cpp new file mode 100644 index 0000000..3051bab --- /dev/null +++ b/toonz/sources/common/trop/tresample.cpp @@ -0,0 +1,5307 @@ + + +#include "tmachine.h" +#include "tpixelgr.h" +#include "quickputP.h" + +//#include "tspecialstyleid.h" +#include "tsystem.h" + +#include "tcolorstyles.h" +#include "tpixelutils.h" +//#include "tstopwatch.h" +#ifndef TNZCORE_LIGHT +#include "tpalette.h" +#include "trastercm.h" +#include "tropcm.h" +#endif + +using namespace TConsts; + +#ifdef WIN32 +#include // per SSE2 +#endif + +//=========================================================================== +/* +Versione con estensione dell'ultimo pixel e con default_value +per pixel "fuori" dall'immagine di ingresso. + + +Sistemi di coordinate a meno di una traslazione: + +UV: coordinate dell'immagine di partenza +ST: coordinate di filtro (il raggio del filtro e' intero in ST) +FG: coordinate del filtro discretizzato +XY: coordinate dell'immagine di arrivo + +Tra UV e ST c'e' una traslazione intera finche' non c'e' ingrandimento +e non c'e' blur. Il blur aggiunge uno scale, e altrettanto fa l'ingrandimento. +Tra ST e FG c'e' uno scale per la risoluzione del filtro. + +Oggetti: +out : pixel di output (centro di ST e FG) +ref : pixel di riferimento dell'immagine di input +pix : pixel contribuente + +Notazione per le coordinate: +obj_x : coordinate intere di obj +obj_x_ : coordinate float di obj + +Notazione per le coppie di coordinate: +obj1_obj2_x : coordinate intere di obj1 rispetto a obj2 +obj1_obj2_x_ : coordinate float di obj1 rispetto a obj2 + +Matrici affini: +aff_xy2uv : matrice di trasformazione delle coordinate da XY a UV +aff0_xy2uv : stessa matrice con la parte di shift messa a 0 + + +Una tantum: + +aff_uv2xy = aff +aff_xy2uv = aff_inv (aff_uv2xy) +aff0_uv2xy = aff_place (00, 00, aff_uv2xy) + vedi il codice, comunque ottimizzo una rotazione seguita da una scalatura + anisotropa. Cerco i fattori di scala facendo radici di somme di quadrati. + Mi regolo sui fattori di scala come se dovessi considerare la scalatura + da sola. In questo modo tutto si riporta alla vecchia maniera se i fattori + di scala sono uguali. Se sono diversi il risultato e' comunque esatto per + rotazioni di multipli di 90 gradi, e non ha discontinuita'. +aff0_uv2st = aff_mult (aff0_xy2st, aff0_uv2xy) +aff0_st2fg = aff_scale (filter_resolution, Aff_I) +aff0_uv2fg = aff_mult (aff0_st2fg, aff0_uv2st) + +pix_ref_uv[] = tutti quelli che servono (vedi sotto) +pix_ref_fg_ = AFF_M_V (aff0_uv2fg, pix_ref_uv) +pix_ref_fg[] = ROUND (pix_ref_fg_) + + +Ciclo su out_xy: + +out_uv_ = AFF_M_V (aff_xy2uv, out_xy) +ref_uv = INT_LE (out_uv_) +ref_out_uv_ = ref_uv - out_uv_ +ref_out_fg_ = AFF_M_V (aff0_uv2fg, ref_out_uv_) +ref_out_fg = ROUND (ref_out_fg_) + +Ciclo sui pix: + +pix_out_fg = pix_ref_fg + ref_out_fg +weight = filter[pix_out_f] * filter[pix_out_g] + + +Per sapere quali sono i pix che servono: + +-filter_fg_radius < pix_out_fg < filter_fg_radius +min_pix_out_uv_ < pix_out_uv_ < max_pix_out_uv_ +min_pix_out_uv_ < pix_ref_uv_ + ref_out_uv_ < max_pix_out_uv_ +min_pix_out_uv_ + out_ref_uv_ < pix_ref_uv_ < max_pix_out_uv_ + out_ref_uv_ +min_pix_out_uv_ < pix_ref_uv_ < max_pix_out_uv_ + 1 +Ciclo su tutti quelli che soddisfano questa condizione + +0 <= out_ref_uv_ < 1 +-1 < ref_out_uv_ <= 0 +min_ref_out_fg_ <= ref_out_fg_ <= max_ref_out_fg_ +min_ref_out_fg <= ref_out_fg <= max_ref_out_fg +-filter_fg_radius < pix_out_fg < filter_fg_radius +-filter_fg_radius < pix_ref_fg + ref_out_fg < filter_fg_radius +-filter_fg_radius - ref_out_fg < pix_ref_fg < filter_fg_radius - ref_out_fg +-filter_fg_radius - max_ref_out_fg < pix_ref_fg < filter_fg_radius - min_ref_out_fg +Scarto quelli che non soddisfano questa condizione + + +Come e' fatto il filtro: + + TOP filter_array[filter_array_size-1] + | filter[max_filter_fg] + | filter[max_pix_out_fg] + | filter[0] + | filter[min_pix_out_fg] + BOT filter[min_filter_fg] == filter_array[0] + +*/ + +//------------------------------------------------------------------------------ +//--------------------------------------------------------------------------- + +#if !defined(TNZ_LITTLE_ENDIAN) +TNZ_LITTLE_ENDIAN undefined !! +#endif + + //2^36 * 1.5, (52-_shiftamt=36) uses limited precision to floor + const double _double2fixmagic = 68719476736.0 * 1.5; + +//16.16 fixed point representation +const TINT32 _shiftamt = 16; + +#if TNZ_LITTLE_ENDIAN +#define iexp_ 1 +#define iman_ 0 +#else +#define iexp_ 0 +#define iman_ 1 +#endif + +inline TINT32 Double2Int(double val) +{ + val = val + _double2fixmagic; + return ((TINT32 *)&val)[iman_] >> _shiftamt; +} + +#define DOUBLE_TO_INT32(D) (d2iaux = D, d2iaux += _double2fixmagic, (((TINT32 *)&(d2iaux))[iman_] >> _shiftamt)) + +//#define USE_DOUBLE_TO_INT + +//=========================================================================== + +inline double sinc0(double x, int a) +{ + return sin((pi / (a)) * (x)) / ((pi / (a)) * (x)); +} + +inline double sinc(double x, int a) +{ + return (x) == 0.0 ? 1.0 : sin((pi / (a)) * (x)) / ((pi / (a)) * (x)); +} + +inline UCHAR TO8BIT(float X) +{ + return (((X) < 0.0F) ? 0 : (((X) > 255.0F) ? 255 : tround(X))); +} + +const UCHAR BORDER_GR8 = 255; +const UCHAR GREY_GR8 = 127; + +#ifdef USE_INLINE_FUNS + +//--------------------------------------------------------------------------- + +inline double aff0MV1(const TAffine &aff, double v1, double v2) +{ + return aff.a11 * v1 + aff.a12 * v2; +} + +//--------------------------------------------------------------------------- + +inline double affMV1(const TAffine &aff, double v1, double v2) +{ + return aff.a11 * v1 + aff.a12 * v2 + aff.a13; +} + +//--------------------------------------------------------------------------- + +inline double aff0MV2(const TAffine &aff, double v1, double v2) +{ + return aff.a21 * v1 + aff.a22 * v2; +} + +//--------------------------------------------------------------------------- + +inline double affMV2(const TAffine &aff, double v1, double v2) +{ + return aff.a21 * v1 + aff.a22 * v2 + aff.a23; +} + +#else // !USE_INLINE_FUNS + +#ifndef USE_DOUBLE_TO_INT +#define ROUND(x) ((int)(((int)(-0.9F) == 0 && (x) < 0.0F) ? ((x)-0.5F) : ((x) + 0.5F))) +#define ROUNDP(x) ((int)((x) + 0.5F)) +#define FLOOR(x) ((int)(x) > (x) ? (int)(x)-1 : (int)(x)) +#define CEIL(x) ((int)(x) < (x) ? (int)(x) + 1 : (int)(x)) +#else +#define ROUND(x) (DOUBLE_TO_INT32(((int)(-0.9F) == 0 && (x) < 0.0F) ? ((x)-0.5F) : ((x) + 0.5F))) +#define ROUNDP(x) (DOUBLE_TO_INT32((x) + 0.5F)) +#define FLOOR(x) (DOUBLE_TO_INT32(x) > (x) ? DOUBLE_TO_INT32(x) - 1 : DOUBLE_TO_INT32(x)) +#define CEIL(x) (DOUBLE_TO_INT32(x) < (x) ? DOUBLE_TO_INT32(x) + 1 : DOUBLE_TO_INT32(x)) +#endif + +#define INTLE(x) (FLOOR(x)) +#define INTGT(x) (FLOOR(x) + 1) +#define INTLT(x) (CEIL(x) - 1) +#define INTGE(x) (CEIL(x)) + +#define NOT_LESS_THAN(MIN, X) \ + { \ + if ((X) < (MIN)) \ + (X) = (MIN); \ + } +#define NOT_MORE_THAN(MAX, X) \ + { \ + if ((X) > (MAX)) \ + (X) = (MAX); \ + } + +#define tround ROUND +#define troundp ROUNDP +#define tfloor FLOOR +#define tceil CEIL + +#define intLE INTLE +#define intGT INTGT +#define intLT INTLT +#define intGE INTGE + +#define notLessThan NOT_LESS_THAN +#define notMoreThan NOT_MORE_THAN + +#define AFF0_M_V_1(AFF, V1, V2) ((AFF).a11 * (V1) + (AFF).a12 * (V2)) +#define AFF0_M_V_2(AFF, V1, V2) ((AFF).a21 * (V1) + (AFF).a22 * (V2)) + +#define AFF_M_V_1(AFF, V1, V2) ((AFF).a11 * (V1) + (AFF).a12 * (V2) + (AFF).a13) +#define AFF_M_V_2(AFF, V1, V2) ((AFF).a21 * (V1) + (AFF).a22 * (V2) + (AFF).a23) + +#define aff0MV1 AFF0_M_V_1 +#define aff0MV2 AFF0_M_V_2 + +#define affMV1 AFF_M_V_1 +#define affMV2 AFF_M_V_2 + +#endif // USE_INLINE_FUNS + +//--------------------------------------------------------------------------- + +struct FILTER { + int first, last; + float *w; + float *w_base; +}; + +struct NOCALC { + int first, last; +}; + +//--------------------------------------------------------------------------- +inline int get_filter_radius(TRop::ResampleFilterType flt_type) +{ + switch (flt_type) { + case TRop::Triangle: + return 1; + case TRop::Mitchell: + return 2; + case TRop::Cubic5: + return 2; + case TRop::Cubic75: + return 2; + case TRop::Cubic1: + return 2; + case TRop::Hann2: + return 2; + case TRop::Hann3: + return 3; + case TRop::Hamming2: + return 2; + case TRop::Hamming3: + return 3; + case TRop::Lanczos2: + return 2; + case TRop::Lanczos3: + return 3; + case TRop::Gauss: + return 2; + default: + assert(!"bad filter type"); + } + return 0; +} + +//--------------------------------------------------------------------------- + +//!Equivalent to aff * TRectD(u0, v0, u1, v0). +inline void minmax(double u0, double v0, + double u1, double v1, const TAffine &aff, + double &x0, double &y0, + double &x1, double &y1) +{ + double xmin, ymin; + double xmax, ymax; + double x_a, y_a; + double x_b, y_b; + double x_c, y_c; + double x_d, y_d; + + x_a = affMV1(aff, u0, v0); + y_a = affMV2(aff, u0, v0); + x_b = affMV1(aff, u1, v0); + y_b = affMV2(aff, u1, v0); + x_c = affMV1(aff, u1, v1); + y_c = affMV2(aff, u1, v1); + x_d = affMV1(aff, u0, v1); + y_d = affMV2(aff, u0, v1); + xmin = tmin(x_a, x_b); + xmax = tmax(x_a, x_b); + xmin = tmin(xmin, x_c); + xmax = tmax(xmax, x_c); + xmin = tmin(xmin, x_d); + xmax = tmax(xmax, x_d); + ymin = tmin(y_a, y_b); + ymax = tmax(y_a, y_b); + ymin = tmin(ymin, y_c); + ymax = tmax(ymax, y_c); + ymin = tmin(ymin, y_d); + ymax = tmax(ymax, y_d); + x0 = xmin; + y0 = ymin; + x1 = xmax; + y1 = ymax; +} + +/*---------------------------------------------------------------------------*/ + +/* +inline bool trivial_rot (TAffine inv, int *dudx, int *dudy, + int *dvdx, int *dvdy) +{ +*dudx = 0; +*dudy = 0; +*dvdx = 0; +*dvdy = 0; +if (! (inv.a12 == 0 && inv.a21 == 0 || inv.a11 == 0 && inv.a22 == 0)) + return false; +if (! (inv.a11 == 1 || inv.a11 == 0 || inv.a11 == -1)) + return false; +if (! (inv.a12 == 1 || inv.a12 == 0 || inv.a12 == -1)) + return false; +if (! (inv.a21 == 1 || inv.a21 == 0 || inv.a21 == -1)) + return false; +if (! (inv.a22 == 1 || inv.a22 == 0 || inv.a22 == -1)) + return false; +*dudx = (int)inv.a11; +*dudy = (int)inv.a12; +*dvdx = (int)inv.a21; +*dvdy = (int)inv.a22; +return true; +} +*/ + +//----------------------------------------------------------------------------- + +// +// see Mitchell&Netravali, "Reconstruction Filters in Computer Graphics", +// SIGGRAPH 88. Mitchell code provided by Paul Heckbert. +// +// + +//----------------------------------------------------------------------------- + +static double p0, p2, p3, q0, q1, q2, q3; + +inline void mitchellinit(double b, double c) +{ + p0 = (6.0 - 2.0 * b) / 6.0; + p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0; + p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0; + q0 = (8.0 * b + 24.0 * c) / 6.0; + q1 = (-12.0 * b - 48.0 * c) / 6.0; + q2 = (6.0 * b + 30.0 * c) / 6.0; + q3 = (-b - 6.0 * c) / 6.0; +} + +const int fltradMitchell = 2; +static inline double flt_mitchell(double x) /*Mitchell & Netravali's two-param cubic*/ +{ + static int mitfirsted; + + if (!mitfirsted) { + mitchellinit(1.0 / 3.0, 1.0 / 3.0); + mitfirsted = 1; + } + if (x < -2.0) + return 0.0; + if (x < -1.0) + return (q0 - x * (q1 - x * (q2 - x * q3))); + if (x < 0.0) + return (p0 + x * x * (p2 - x * p3)); + if (x < 1.0) + return (p0 + x * x * (p2 + x * p3)); + if (x < 2.0) + return (q0 + x * (q1 + x * (q2 + x * q3))); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradTriangle = 1; +static inline double flt_triangle(double x) +{ + if (x < -1.0) + return 0.0; + if (x < 0.0) + return 1.0 + x; + if (x < 1.0) + return 1.0 - x; + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradCubic5 = 2; +static inline double flt_cubic_5(double x) +{ + if (x < 0.0) + x = -x; + if (x < 1.0) + return 2.5 * x * x * x - 3.5 * x * x + 1; + if (x < 2.0) + return 0.5 * x * x * x - 2.5 * x * x + 4 * x - 2; + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradCubic75 = 2; +static inline double flt_cubic_75(double x) +{ + if (x < 0.0) + x = -x; + if (x < 1.0) + return 2.75 * x * x * x - 3.75 * x * x + 1; + if (x < 2.0) + return 0.75 * x * x * x - 3.75 * x * x + 6 * x - 3; + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradCubic1 = 2; +static inline double flt_cubic_1(double x) +{ + if (x < 0.0) + x = -x; + if (x < 1.0) + return 3 * x * x * x - 4 * x * x + 1; + if (x < 2.0) + return x * x * x - 5 * x * x + 8 * x - 4; + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradHann2 = 2; +static inline double flt_hann2(double x) +{ + if (x <= -2.0) + return 0.0; + if (x < 2.0) + return sinc(x, 1) * (0.5 + 0.5 * cos((pi / 2) * x)); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradHann3 = 3; +static inline double flt_hann3(double x) +{ + if (x <= -3.0) + return 0.0; + if (x < 3.0) + return sinc(x, 1) * (0.5 + 0.5 * cos((pi / 3) * x)); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradHamming2 = 2; +static inline double flt_hamming2(double x) +{ + if (x <= -2.0) + return 0.0; + if (x < 2.0) + return sinc(x, 1) * (0.54 + 0.46 * cos((pi / 2) * x)); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradHamming3 = 3; +static inline double flt_hamming3(double x) +{ + if (x <= -3.0) + return 0.0; + if (x < 3.0) + return sinc(x, 1) * (0.54 + 0.46 * cos((pi / 3) * x)); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradLanczos2 = 2; +static inline double flt_lanczos2(double x) +{ + if (x <= -2.0) + return 0.0; + if (x < 2.0) + return sinc(x, 1) * sinc(x, 2); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradLanczos3 = 3; +static inline double flt_lanczos3(double x) +{ + if (x <= -3.0) + return 0.0; + if (x < 3.0) + return sinc(x, 1) * sinc(x, 3); + return 0.0; +} + +//----------------------------------------------------------------------------- + +const int fltradGauss = 2; +static inline double flt_gauss(double x) +{ + if (x <= -2.0) + return 0.0; + if (x < 2.0) + return exp((-pi) * x * x); + return 0.0; /* exp(-M_PI*2*2)~=3.5*10^-6 */ +} + +//----------------------------------------------------------------------------- + +const int fltradW1 = 2; +static inline double flt_w_1(double x) +{ + if (x < 0.0) + x = -x; + if (x < 0.5) + return 1 - 0.5 * x; + if (x < 1.0) + return 1.5 - 1.5 * x; + if (x < 1.5) + return 0.5 - 0.5 * x; + if (x < 2.0) + return 0.5 * x - 1.0; + return 0.0; +} + +//----------------------------------------------------------------------------- + +static inline void get_flt_fun_rad(TRop::ResampleFilterType flt_type, + double (**flt_fun)(double), double &flt_rad) +{ + double (*fun)(double); + double rad; + + switch (flt_type) { + case TRop::Triangle: + fun = flt_triangle; + rad = fltradTriangle; + CASE TRop::Mitchell : fun = flt_mitchell; + rad = fltradMitchell; + CASE TRop::Cubic5 : fun = flt_cubic_5; + rad = fltradCubic5; + CASE TRop::Cubic75 : fun = flt_cubic_75; + rad = fltradCubic75; + CASE TRop::Cubic1 : fun = flt_cubic_1; + rad = fltradCubic1; + CASE TRop::Hann2 : fun = flt_hann2; + rad = fltradHann2; + CASE TRop::Hann3 : fun = flt_hann3; + rad = fltradHann3; + CASE TRop::Hamming2 : fun = flt_hamming2; + rad = fltradHamming2; + CASE TRop::Hamming3 : fun = flt_hamming3; + rad = fltradHamming3; + CASE TRop::Lanczos2 : fun = flt_lanczos2; + rad = fltradLanczos2; + CASE TRop::Lanczos3 : fun = flt_lanczos3; + rad = fltradLanczos3; + CASE TRop::Gauss : fun = flt_gauss; + rad = fltradGauss; + CASE 101 : fun = flt_w_1; + rad = fltradW1; + DEFAULT: + fun = flt_triangle; + rad = fltradTriangle; + } + if (flt_fun) + *flt_fun = fun; + flt_rad = rad; +} + +//--------------------------------------------------------------------------- + +static FILTER *create_filter(TRop::ResampleFilterType flt_type, double blur, + double dx_du, double delta_x, int lx, + double &xrad, int &umin, int &umax, int &uwidth) +{ + double (*flt_fun)(double); + FILTER *filter, *f; + double du_dx; + int x; + double u_; + int u, ulo, uhi, ulomin, uhimax, m, n, nmax; + double flt_rad, rad_u, rad_x, nodedist_u, nodefreq_u, sum, norm, w; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + get_flt_fun_rad(flt_type, &flt_fun, flt_rad); + du_dx = 1 / dx_du; + if (dx_du > 1) + nodedist_u = blur; /* magnification */ + else + nodedist_u = du_dx * blur; /* minification */ + rad_u = flt_rad * nodedist_u; + rad_x = rad_u * dx_du; + nodefreq_u = 1 / nodedist_u; + /* +mu = lu - 1; +*/ + filter = new FILTER[lx]; + nmax = 0; + ulomin = c_maxint - 1; + uhimax = c_minint + 1; + for (x = 0; x < lx; x++) { + f = filter + x; + u_ = (x - delta_x) * du_dx; + ulo = intGT(u_ - rad_u); + uhi = intLT(u_ + rad_u); + /* + NOT_LESS_THAN( 0, ulo) + NOT_MORE_THAN(mu, uhi) +*/ + m = uhi - ulo + 1; + if (m > 0) { + f->w_base = new float[m]; + f->w = f->w_base - ulo; + for (sum = 0.0, u = ulo; u <= uhi; u++) { + w = (*flt_fun)((u - u_) * nodefreq_u); + sum += w; + f->w[u] = (float)w; + } + for (; ulo <= uhi; ulo++) + if (f->w[ulo]) + break; + for (; uhi >= ulo; uhi--) + if (f->w[uhi]) + break; + if (ulo < ulomin) + ulomin = ulo; + if (uhi > uhimax) + uhimax = uhi; + n = uhi - ulo + 1; + if (n > nmax) + nmax = n; + f->first = ulo; + f->last = uhi; + norm = 1 / sum; + for (u = ulo; u <= uhi; u++) + f->w[u] *= (float)norm; + } else { + f->w_base = 0; + f->first = ulo; + f->last = uhi; + } + } + xrad = rad_x; + umin = ulomin; + umax = uhimax; + uwidth = nmax; + return filter; +} + +//----------------------------------------------------------------------------- + +static NOCALC *create_nocalc(TRop::ResampleFilterType flt_type, double blur, + double dx_du, double delta_x, int lx, + int umin, int umax, int &xwidth) +{ + /* + +Il nocalc serve a stabilire che un insieme di pixel u (di ingresso) +non ha bisogno di essere calcolato, perche tutti i pixel x (di uscita) +su cui questo insieme si distribuisce non hanno bisogno di essere calcolati. + +Il significato del nocalc a coordinata x e': +se arrivati a x si e' trovata una sequenza di width pixel x che +non e' necessario calcolare, allora non e' necessario calcolare tutti i +pixel u da nocalc->first a nocalc->last. + +Per primo va calcolata la width. Deve essere garantito che tutti i pixel u +siano coperti dal vettore di nocalc. +Un pixel u viene usato da un intervallo di x largo quanto il filtro (in x), +cioe' un intervallo aperto (-radx_,radx_) intorno a x(u). +Aggiungendo una unita' x a questa larghezza si ha una larghezza tale che +se tutti i pixel x sono nocalc, un intervallo largo 1 in unita' x +di pixel u non necessita di essere calcolato. +Vogliamo che ulo_ <= first <= last < uhi_ con uhi_ = ulo_ + u(1). +Devono essere nocalc almeno gli x in (x(ulo_)-radx_, x(uhi_)+radx_). +Poniamo x = x(uhi_)+radx_-1. +uhi_ = u(x-radx_+1) +ulo_ = u(x-radx_) +x(ulo_)-radx_ = x-2*radx_ ma questo punto e' escluso, quindi l'intero GT e' +x - width + 1 = INT_LE (x-2*radx_+1) +1 - INT_LE (-2*radx_+1) = width +1 + INT_GE (2*radx_-1) = width +INT_GE (2*radx_) = width +Pero' per sicurezza facciamo +INT_GT (2*radx_) = width + +*/ + + NOCALC *nocalc; + int width; + double flt_rad; + double rad_x; + double du_dx; + double ulo_, uhi_; + int ulo, uhi; + int x; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + du_dx = 1 / dx_du; + get_flt_fun_rad(flt_type, 0, flt_rad); + if (dx_du > 1) /* sto ingrandendo */ + rad_x = flt_rad * blur * dx_du; + else + rad_x = flt_rad * blur; + rad_x += 0.5; /* ?!?!?!?!? */ + width = intGT(2 * rad_x + 1); + nocalc = new NOCALC[lx + width - 1]; + for (x = 0; x < lx + width - 1; x++) { + ulo_ = (x - rad_x - delta_x) * du_dx; + uhi_ = ulo_ + du_dx; + ulo = intGE(ulo_); + uhi = intLT(uhi_); + nocalc[x].first = tmax(umin, ulo); + nocalc[x].last = tmin(umax, uhi); + } + xwidth = width; + + return nocalc; +} + +//--------------------------------------------------------------------------- +/* +inline UINT calcValueInit(UINT init_value){ + return init_value;//0xffffU; +} +*/ +/* +inline void calcValueInit(UINT &calc_value){ + calc_value = 0xffffU; +} + +inline bool calcValueEmpty(UINT calc_value){ + return calc_value == 0xffffU; +} +inline bool calcValueReady(UINT calc_value){ + return calc_value <= 0x1ffU; +} + +inline void calcValueAdvance(UINT &calc_value){ + calc_value >>= 1; +} + +inline void calcValueNoCalc(UINT &calc_value){ + calc_value &= ~0x80U; +} +*/ +#define CALC_VALUE_INIT \ + { \ + calc_value = 0xffffU; \ + } +#define CALC_VALUE_EMPTY (calc_value == 0xffffU) +#define CALC_VALUE_READY (calc_value <= 0x1ffU) +#define CALC_VALUE_ADVANCE \ + { \ + calc_value >>= 1; \ + } +#define CALC_VALUE_NOCALC \ + { \ + calc_value &= ~0x80U; \ + } + +///////////////////////////////////////////////////////// +// INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE +///////////////////////////////////////////////////////// + +//#define PIXVAL_EQ_EQUAL(V1,V2) ((V1)==(V2)) + +/* +#define PIXVAL_EQ_24_EQUAL(V1,V2) ((V1)&0xffffff==(V2)&0xffffff) +#define PIXVAL_EQ_2_LONG_EQUAL(V1,V2) (*(TINT32*)&(V1)==*(TINT32*)&(V2) &&\ + ((TINT32*)&(V1))[1]==((TINT32*)&(V2))[1]) +*/ +///////////////////////////////////////////////////////// +// fine GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE +///////////////////////////////////////////////////////// + +//#define PIXVAL_EQ_LONG_EQUAL(V1,V2) (*(TINT32*)&(V1)==*(TINT32*)&(V2)) +//#define PIXVAL_EQ PIXVAL_EQ_LONG_EQUAL +//#define PIXVAL_EQ PIXVAL_EQ_EQUAL + +template +#ifdef WIN32 +__forceinline +#endif + void + ResampleCalcAlgo(PixType *buffer_in, int lu, int lv, int wrap_in, int max_pix_ref_u, int min_pix_ref_u, int max_pix_ref_v, int min_pix_ref_v, UCHAR *calc, int calc_bytesize, int calc_bytewrap) +/* +lu = width +lv = height +wrap_in = wrap +*/ +{ + PixType *prev_line_in; + PixType *last_line_in; + PixType prev_value; + PixType left_value; + PixType last_value; + + UINT calc_value; + UCHAR *calc_byte = 0; + int goodcols; + int *col_height = new int[lu]; + int ref_u, ref_v; + int filter_diam_u = max_pix_ref_u - min_pix_ref_u + 1; + int filter_diam_v = max_pix_ref_v - min_pix_ref_v + 1; + int last_u, last_v; + + int *ch; + int *ch_end; + + assert(col_height); + + CALC_VALUE_INIT + ch = col_height; + ch_end = ch + lu; + + while (ch < ch_end) { + *ch = filter_diam_v; + ++ch; + } + + last_line_in = buffer_in; + for (last_v = 1, ref_v = last_v - max_pix_ref_v; ref_v < 0; last_v++, ref_v++) { + prev_line_in = last_line_in; + last_line_in = buffer_in + last_v * wrap_in; + for (last_u = 0; last_u < lu; last_u++) { + last_value = last_line_in[last_u]; + prev_value = prev_line_in[last_u]; + if (last_value == prev_value) + col_height[last_u]++; + else + col_height[last_u] = 1; + } + } + + for (; last_v < lv; last_v++, ref_v++) { + prev_line_in = last_line_in; + last_line_in = buffer_in + last_v * wrap_in; + last_value = last_line_in[0]; + goodcols = 0; + for (last_u = 0, ref_u = last_u - max_pix_ref_u; ref_u < 0; last_u++, ref_u++) { + left_value = last_value; + last_value = last_line_in[last_u]; + prev_value = prev_line_in[last_u]; + if (last_value == prev_value) { + col_height[last_u]++; + if (col_height[last_u] >= filter_diam_v) + if (last_value == left_value) + goodcols++; + else + goodcols = 1; + else + goodcols = 0; + } else { + col_height[last_u] = 1; + goodcols = 0; + } + } + calc_byte = calc + calc_bytewrap * ref_v; + CALC_VALUE_INIT + for (; last_u < lu; last_u++, ref_u++) { + left_value = last_value; + last_value = last_line_in[last_u]; + prev_value = prev_line_in[last_u]; + if (last_value == prev_value) { + col_height[last_u]++; + if (col_height[last_u] >= filter_diam_v) + if (last_value == left_value) { + goodcols++; + if (goodcols >= filter_diam_u) + CALC_VALUE_NOCALC + } else + goodcols = 1; + else + goodcols = 0; + } else { + col_height[last_u] = 1; + goodcols = 0; + } + if (CALC_VALUE_READY) { + *calc_byte++ = (UCHAR)calc_value; + CALC_VALUE_INIT + } else + CALC_VALUE_ADVANCE + } + for (; ref_u < lu; last_u++, ref_u++) { + if (CALC_VALUE_READY) { + *calc_byte++ = (UCHAR)calc_value; + CALC_VALUE_INIT + } else + CALC_VALUE_ADVANCE + } + if (!CALC_VALUE_EMPTY) { + while (!CALC_VALUE_READY) + CALC_VALUE_ADVANCE + *calc_byte++ = (UCHAR)calc_value; + } + } + + for (; ref_v < lv; last_v++, ref_v++) { + for (last_u = 0, ref_u = last_u - max_pix_ref_u; ref_u < 0; last_u++, ref_u++) { + } + calc_byte = calc + calc_bytewrap * ref_v; + CALC_VALUE_INIT + for (; last_u < lu; last_u++, ref_u++) { + if (CALC_VALUE_READY) { + *calc_byte++ = (UCHAR)calc_value; + CALC_VALUE_INIT + } else + CALC_VALUE_ADVANCE + } + for (; ref_u < lu; last_u++, ref_u++) { + if (CALC_VALUE_READY) { + *calc_byte++ = (UCHAR)calc_value; + CALC_VALUE_INIT + } else + CALC_VALUE_ADVANCE + } + if (!CALC_VALUE_EMPTY) { + while (!CALC_VALUE_READY) + CALC_VALUE_ADVANCE + *calc_byte++ = (UCHAR)calc_value; + } + } + assert(!calc_byte || calc_byte == calc + calc_bytesize); + + if (col_height) + delete[] col_height; +} + +/*---------------------------------------------------------------------------*/ + +template +void create_calc(const TRasterPT &rin, + int min_pix_ref_u, int max_pix_ref_u, + int min_pix_ref_v, int max_pix_ref_v, + UCHAR *&p_calc, int &p_calc_allocsize, int &p_calc_bytewrap) +{ + UCHAR *calc; + int lu, lv; + int wrap_in; + int calc_bytesize; + int calc_bytewrap; + + lu = rin->getLx(); + lv = rin->getLy(); + wrap_in = rin->getWrap(); + + p_calc_bytewrap = (lu + 7) >> 3; // ceil(lu/8) + calc_bytewrap = p_calc_bytewrap; + calc_bytesize = calc_bytewrap * lv; // lv * ceil(lu/8) + if (calc_bytesize > p_calc_allocsize) { + if (p_calc_allocsize) + delete[](p_calc); + //TMALLOC (*p_calc, calc_bytesize) + p_calc = new UCHAR[calc_bytesize]; + assert(p_calc); + memset(p_calc, 0xff, calc_bytesize); + p_calc_allocsize = calc_bytesize; + } + calc = p_calc; + if (lu < max_pix_ref_u + 1 || lv < max_pix_ref_v + 1) { + memset(calc, 0xff, calc_bytesize); + return; + } + + //RESAMPLE_CALC_ALGO + + ResampleCalcAlgo(rin->pixels(), lu, lv, wrap_in, max_pix_ref_u, min_pix_ref_u, + max_pix_ref_v, min_pix_ref_v, calc, calc_bytesize, calc_bytewrap); + + //for (int i=0;itype) + { + case RAS_CM16: + { + USHORT *buffer_in; + USHORT *prev_line_in; + USHORT *last_line_in; + USHORT prev_value; + USHORT left_value; + USHORT last_value; + + buffer_in = rin->buffer; + +#undef PIXVAL_EQ +#define PIXVAL_EQ PIXVAL_EQ_EQUAL + + RESAMPLE_CALC_ALGO + + } + + CASE RAS_CM24: + { + ULONG *buffer_in; + ULONG *prev_line_in; + ULONG *last_line_in; + ULONG prev_value; + ULONG left_value; + ULONG last_value; + + buffer_in = rin->buffer; + +#undef PIXVAL_EQ +#define PIXVAL_EQ PIXVAL_EQ_24_EQUAL + + RESAMPLE_CALC_ALGO + + } + + CASE RAS_RGB_: // si potrebbe ignorare M + __OR RAS_RGBM: + { + LPIXEL *buffer_in; + LPIXEL *prev_line_in; + LPIXEL *last_line_in; + LPIXEL prev_value; + LPIXEL left_value; + LPIXEL last_value; + + buffer_in = rin->buffer; + +#undef PIXVAL_EQ +#define PIXVAL_EQ PIXVAL_EQ_LONG_EQUAL + + RESAMPLE_CALC_ALGO + + } + + CASE RAS_RGBM64: + { + SPIXEL *buffer_in; + SPIXEL *prev_line_in; + SPIXEL *last_line_in; + SPIXEL prev_value; + SPIXEL left_value; + SPIXEL last_value; + + buffer_in = rin->buffer; + +#undef PIXVAL_EQ +#define PIXVAL_EQ PIXVAL_EQ_2_LONG_EQUAL + + RESAMPLE_CALC_ALGO + + } + + DEFAULT: + assert ( !"invalid raster type"); + } +*/ + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// +} + +//--------------------------------------------------------------------------- + +namespace +{ + +template +class Converter +{ +public: + static inline T convert(const TPixel32 &pixin) + { + return pixin; + } +}; + +#define BYTE_FROM_USHORT(u) (((256U * 255U + 1U) * u + (1 << 23)) >> 24) +#define USHORT_FROM_BYTE(u) (u | u << 8) + +template <> +class Converter +{ +public: + static inline TPixel64 convert(const TPixel32 &pix) + { + return TPixel64( + USHORT_FROM_BYTE(pix.r), + USHORT_FROM_BYTE(pix.g), + USHORT_FROM_BYTE(pix.b), + USHORT_FROM_BYTE(pix.m)); + } +}; + +//----------------------------------------------------------------------------- + +inline double get_filter_value(TRop::ResampleFilterType flt_type, double x) +{ + //it is assumed that x != 0 (not checked only for speed reasons) + switch (flt_type) { + case TRop::Triangle: + if (x < -1.0) + return 0.0; + if (x < 0.0) + return 1.0 + x; + if (x < 1.0) + return 1.0 - x; + return 0.0; + + case TRop::Mitchell: { + static double p0, p2, p3, q0, q1, q2, q3; + + if (!p0) { + const double b = 1.0 / 3.0; + const double c = 1.0 / 3.0; + + p0 = (6.0 - 2.0 * b) / 6.0; + p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0; + p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0; + q0 = (8.0 * b + 24.0 * c) / 6.0; + q1 = (-12.0 * b - 48.0 * c) / 6.0; + q2 = (6.0 * b + 30.0 * c) / 6.0; + q3 = (-b - 6.0 * c) / 6.0; + } + if (x < -2.0) + return 0.0; + if (x < -1.0) + return (q0 - x * (q1 - x * (q2 - x * q3))); + if (x < 0.0) + return (p0 + x * x * (p2 - x * p3)); + if (x < 1.0) + return (p0 + x * x * (p2 + x * p3)); + if (x < 2.0) + return (q0 + x * (q1 + x * (q2 + x * q3))); + } + + CASE TRop::Cubic5 : if (x < 0.0) x = -x; + if (x < 1.0) + return 2.5 * x * x * x - 3.5 * x * x + 1; + if (x < 2.0) + return 0.5 * x * x * x - 2.5 * x * x + 4 * x - 2; + + CASE TRop::Cubic75 : if (x < 0.0) x = -x; + if (x < 1.0) + return 2.75 * x * x * x - 3.75 * x * x + 1; + if (x < 2.0) + return 0.75 * x * x * x - 3.75 * x * x + 6 * x - 3; + + CASE TRop::Cubic1 : if (x < 0.0) x = -x; + if (x < 1.0) + return 3 * x * x * x - 4 * x * x + 1; + if (x < 2.0) + return x * x * x - 5 * x * x + 8 * x - 4; + + CASE TRop::Hann2 : if (x <= -2.0) return 0.0; + if (x < 2.0) + return sinc0(x, 1) * (0.5 + 0.5 * cos((pi / 2) * x)); + + CASE TRop::Hann3 : if (x <= -3.0) return 0.0; + if (x < 3.0) + return sinc0(x, 1) * (0.5 + 0.5 * cos((pi / 3) * x)); + + CASE TRop::Hamming2 : if (x <= -2.0) return 0.0; + if (x < 2.0) + return sinc0(x, 1) * (0.54 + 0.46 * cos((pi / 2) * x)); + + CASE TRop::Hamming3 : if (x <= -3.0) return 0.0; + if (x < 3.0) + return sinc0(x, 1) * (0.54 + 0.46 * cos((pi / 3) * x)); + + CASE TRop::Lanczos2 : if (x <= -2.0) return 0.0; + if (x < 2.0) + return sinc0(x, 1) * sinc0(x, 2); + + CASE TRop::Lanczos3 : if (x <= -3.0) return 0.0; + if (x < 3.0) + return sinc0(x, 1) * sinc0(x, 3); + + CASE TRop::Gauss : if (x <= -2.0) return 0.0; + if (x < 2.0) + return exp((-pi) * x * x); + /* exp(-M_PI*2*2)~=3.5*10^-6 */ + + DEFAULT: + assert(!"bad filter type"); + } + return 0.0; +} + +//--------------------------------------------------------------------------- +template +void resample_clear_rgbm(TRasterPT rout, T default_value) +{ + T *buffer_out; + buffer_out = rout->pixels(); + for (int out_y = 0; out_y < rout->getLy(); out_y++) + for (int out_x = 0; out_x < rout->getLx(); out_x++) + buffer_out[out_x + out_y * rout->getWrap()] = default_value; +} + +//--------------------------------------------------------------------------- + +template +void resample_main_rgbm(TRasterPT rout, const TRasterPT &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter) +{ + const T *buffer_in; + T *buffer_out; + T *pix_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int filter_mu, filter_mv; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + int outside_min_u, outside_min_v; + int outside_max_u, outside_max_v; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + T pix_value, default_value(0, 0, 0, 0); + SUMS_TYPE weight, sum_weights; + double inv_sum_weights; + SUMS_TYPE sum_contribs_r, sum_contribs_g, sum_contribs_b, sum_contribs_m; + double out_fval_r, out_fval_g, out_fval_b, out_fval_m; + int out_value_r, out_value_g, out_value_b, out_value_m; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + rout->clear(); + return; + } + + calc = 0; + calc_allocsize = 0; + + //Create a bit array, each indicating whether a pixel has to be calculated or not + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + filter_mu = max_pix_ref_u - min_pix_ref_u; + filter_mv = max_pix_ref_v - min_pix_ref_v; + inside_limit_u = lu - filter_mu; + inside_limit_v = lv - filter_mv; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u = -max_pix_ref_u; + outside_min_v = -max_pix_ref_v; + outside_max_u = mu - min_pix_ref_u; + outside_max_v = mv - min_pix_ref_v; + + //For every pixel of the output image + for (out_y = 0, out_y_ = 0.5; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.5; out_x < lx; out_x++, out_x_ += 1.0) { + pix_out = buffer_out + out_y * wrap_out + out_x; + + //Take the pre-image of the pixel through the passed affine + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + + //Convert to integer coordinates + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + //NOTE: The following condition is equivalent to: + // (ref_u + min_pix_ref_u >= 0 && ref_v + min_pix_ref_v >= 0 && + // ref_u + max_pix_ref_u < lu && ref_v + max_pix_ref_v < lv) + // - since the presence of (UINT) makes integeres < 0 become >> 0 + if (inside_nonempty && + (UINT)(ref_u + min_pix_ref_u) < inside_limit_u && + (UINT)(ref_v + min_pix_ref_v) < inside_limit_v) { + //The filter mask starting around (ref_u, ref_v) is completely contained + //in the source raster + + //Get the calculation array mask byte + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) //If the mask bit for this pixel is on + { + ref_out_u_ = ref_u - out_u_; //Fractionary part of the pre-image + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); //Make the image of it into fg + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); //Convert to integer coordinates + ref_out_g = tround(ref_out_g_); + + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + //Make the weighted sum of source pixels + for (i = n_pix - 1; i >= 0; --i) { + //Build the weight for this pixel + pix_out_f = pix_ref_f[i] + ref_out_f; //image of the integer part + that of the fractionary part + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + + //Add the weighted pixel contribute + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + sum_contribs_r += (SUMS_TYPE)pix_value.r * weight; + sum_contribs_g += (SUMS_TYPE)pix_value.g * weight; + sum_contribs_b += (SUMS_TYPE)pix_value.b * weight; + sum_contribs_m += (SUMS_TYPE)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + } else + //The pixel is copied from the corresponding source... + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else if (outside_min_u <= ref_u && ref_u <= outside_max_u && + outside_min_v <= ref_v && ref_v <= outside_max_v) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + for (i = n_pix - 1; i >= 0; --i) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + if (pix_u < 0 || pix_u > mu || + pix_v < 0 || pix_v > mv) { + sum_weights += weight; //0-padding + continue; + } + + notLessThan(0, pix_u); //Copy-padding + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + sum_contribs_r += (SUMS_TYPE)pix_value.r * weight; + sum_contribs_g += (SUMS_TYPE)pix_value.g * weight; + sum_contribs_b += (SUMS_TYPE)pix_value.b * weight; + sum_contribs_m += (SUMS_TYPE)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + } else + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else + *pix_out = default_value; + } + } + + delete[] calc; +} + +//--------------------------------------------------------------------------- + +#ifdef WIN32 + +namespace +{ + +__declspec(align(16)) class TPixelFloat +{ +public: + TPixelFloat() : b(0), g(0), r(0), m(0) {} + + TPixelFloat(float rr, float gg, float bb, float mm) + : b(bb), g(gg), r(rr), m(mm) {} + + TPixelFloat(const TPixel32 &pix) + : b(pix.b), g(pix.g), r(pix.r), m(pix.m) {} + + float b, g, r, m; +}; + +} // anonymous namespace + +//--------------------------------------------------------------------------- + +template +void resample_main_rgbm_SSE2(TRasterPT rout, const TRasterPT &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter) +{ + __m128i zeros = _mm_setzero_si128(); + const T *buffer_in; + T *buffer_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int filter_mu, filter_mv; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + //double outside_min_u_, outside_min_v_; + //double outside_max_u_, outside_max_v_; + int outside_min_u, outside_min_v; + int outside_max_u, outside_max_v; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + T pix_value; + T default_value(0, 0, 0, 0); + float weight; + float sum_weights; + float inv_sum_weights; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + T *pix_out; + + __m128 sum_contribs_packed; + + __m128i pix_value_packed_i; + __m128 pix_value_packed; + __m128 weight_packed; + + __m128 zeros2 = _mm_setzero_ps(); + + float maxChannelValue = (float)T::maxChannelValue; + __m128 maxChanneValue_packed = _mm_load1_ps(&maxChannelValue); + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + resample_clear_rgbm(rout, default_value); + return; + } + calc = 0; + calc_allocsize = 0; + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + filter_mu = max_pix_ref_u - min_pix_ref_u; + filter_mv = max_pix_ref_v - min_pix_ref_v; + inside_limit_u = lu - filter_mu; + inside_limit_v = lv - filter_mv; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u = -max_pix_ref_u; + outside_min_v = -max_pix_ref_v; + outside_max_u = mu - min_pix_ref_u; + outside_max_v = mv - min_pix_ref_v; + + for (out_y = 0, out_y_ = 0.5; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.5; out_x < lx; out_x++, out_x_ += 1.0) { + pix_out = buffer_out + out_y * wrap_out + out_x; + + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + if (inside_nonempty && + (UINT)(ref_u + min_pix_ref_u) < inside_limit_u && + (UINT)(ref_v + min_pix_ref_v) < inside_limit_v) { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + + sum_contribs_packed = _mm_setzero_ps(); + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + pix_value_packed_i = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)&pix_value), zeros); + pix_value_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(pix_value_packed_i, zeros)); + + weight_packed = _mm_load1_ps(&weight); + sum_contribs_packed = _mm_add_ps(sum_contribs_packed, _mm_mul_ps(pix_value_packed, weight_packed)); + + sum_weights += weight; + } + + inv_sum_weights = 1.0f / sum_weights; + __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); + + __m128 out_fval_packed = _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); + out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); + out_fval_packed = _mm_min_ps(out_fval_packed, maxChanneValue_packed); + + __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); + out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); + out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); + } else + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else + //if( outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + // outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_ ) + if (outside_min_u <= ref_u && ref_u <= outside_max_u && + outside_min_v <= ref_v && ref_v <= outside_max_v) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_packed = _mm_setzero_ps(); + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + if (pix_u < 0 || pix_u > mu || + pix_v < 0 || pix_v > mv) { + sum_weights += weight; + continue; + } + + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + pix_value_packed_i = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*(DWORD *)&pix_value), zeros); + pix_value_packed = _mm_cvtepi32_ps(_mm_unpacklo_epi16(pix_value_packed_i, zeros)); + + weight_packed = _mm_load1_ps(&weight); + sum_contribs_packed = _mm_add_ps(sum_contribs_packed, _mm_mul_ps(pix_value_packed, weight_packed)); + + sum_weights += weight; + } + inv_sum_weights = 1.0f / sum_weights; + + __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); + __m128 out_fval_packed = _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); + out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); + out_fval_packed = _mm_min_ps(out_fval_packed, maxChanneValue_packed); + + __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); + out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); + out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); + } else + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else { + *pix_out = default_value; + } + } + } + if (calc) + delete[] calc; +} + +namespace +{ + +//--------------------------------------------------------------------------- + +void inline blendBySSE2(TPixel32 *pix_out, + float *ink, float *paint, float *tone, + const __m128 &maxtone_packed, + const __m128i &zeros) +{ + __m128 a_packed = _mm_load_ps(ink); + __m128 b_packed = _mm_load_ps(paint); + + __m128 num_packed = _mm_load1_ps(tone); + __m128 diff_packed = _mm_sub_ps(maxtone_packed, num_packed); + + // calcola in modo vettoriale out = ((den-num)*a + num*b)/den + __m128 pix_value_packed = _mm_mul_ps(diff_packed, a_packed); + __m128 tmpPix_packed = _mm_mul_ps(num_packed, b_packed); + + pix_value_packed = _mm_add_ps(pix_value_packed, tmpPix_packed); + pix_value_packed = _mm_div_ps(pix_value_packed, maxtone_packed); + + // converte i canali da float a char + __m128i pix_value_packed_i = _mm_cvtps_epi32(pix_value_packed); + pix_value_packed_i = _mm_packs_epi32(pix_value_packed_i, zeros); + pix_value_packed_i = _mm_packus_epi16(pix_value_packed_i, zeros); + + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(pix_value_packed_i); +} + +//--------------------------------------------------------------------------- + +void inline blendBySSE2(__m128 &pix_out_packed, + float *ink, float *paint, float *tone, + const __m128 &maxtone_packed, + const __m128i &zeros) +{ + __m128 a_packed = _mm_load_ps(ink); + __m128 b_packed = _mm_load_ps(paint); + + __m128 num_packed = _mm_load1_ps(tone); + __m128 diff_packed = _mm_sub_ps(maxtone_packed, num_packed); + + // calcola in modo vettoriale out = ((den-num)*a + num*b)/den + pix_out_packed = _mm_mul_ps(diff_packed, a_packed); + __m128 tmpPix_packed = _mm_mul_ps(num_packed, b_packed); + + pix_out_packed = _mm_add_ps(pix_out_packed, tmpPix_packed); + pix_out_packed = _mm_div_ps(pix_out_packed, maxtone_packed); +} + +} // namespace + +#endif // WIN32 +//--------------------------------------------------------------------------- + +static void get_prow_gr8(const TRasterGR8P &rin, + double a11, double a12, double a21, double a22, + int pmin, int pmax, int q, float *prow) +{ + UCHAR *bufin_gr8, *in_gr8; + int u, v; + int p, p1, p2; + UINT lu, lv; + UINT mu, mv; + int du, dv; + double u_0, v_0; + double u_, v_; + double fu, fv; + double gu, gv; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + +#ifdef BORDER +#undef BORDER +#endif +#define BORDER BORDER_GR8 + + bufin_gr8 = (UCHAR *)rin->pixels(); + lu = rin->getLx(); + mu = lu - 1; + lv = rin->getLy(); + mv = lv - 1; + du = 1; + dv = rin->getWrap(); + u_0 = a12 * q; + v_0 = a22 * q; + + for (p = pmin; p <= pmax; p++) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = tfloor(u_); + v_ = v_0 + a21 * p; + v = tfloor(v_); + if ((UINT)u < mu && (UINT)v < mv) + break; + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_gr8 = bufin_gr8 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? in_gr8[du] : BORDER) + + fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? in_gr8[du + dv] : BORDER) + + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? in_gr8[0] : BORDER) + + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? in_gr8[dv] : BORDER)); + } + p1 = p; + for (p = pmax; p > p1; p--) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = tfloor(u_); + v_ = v_0 + a21 * p; + v = tfloor(v_); + if ((UINT)u < mu && (UINT)v < mv) + break; + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_gr8 = bufin_gr8 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? in_gr8[du] : BORDER) + + fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? in_gr8[du + dv] : BORDER) + + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? in_gr8[0] : BORDER) + + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? in_gr8[dv] : BORDER)); + } + p2 = p; + for (p = p1; p <= p2; p++) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = (int)(u_); + v_ = v_0 + a21 * p; + v = (int)(v_); + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_gr8 = bufin_gr8 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * in_gr8[du] + fu * fv * in_gr8[du + dv] + + gu * gv * in_gr8[0] + gu * fv * in_gr8[dv]); + } +} + +//--------------------------------------------------------------------------- + +#define grey(PIXEL) (TPixelGR8::from(PIXEL).value) + +static void get_prow_gr8(const TRaster32P &rin, + double a11, double a12, double a21, double a22, + int pmin, int pmax, int q, float *prow) +{ + TPixel *bufin_32, *in_32; + int u, v; + int p, p1, p2; + UINT lu, lv; + UINT mu, mv; + int du, dv; + double u_0, v_0; + double u_, v_; + double fu, fv; + double gu, gv; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + +#ifdef BORDER +#undef BORDER +#endif +#define BORDER BORDER_GR8 + + bufin_32 = (TPixel *)rin->pixels(); + lu = rin->getLx(); + mu = lu - 1; + lv = rin->getLy(); + mv = lv - 1; + du = 1; + dv = rin->getWrap(); + u_0 = a12 * q; + v_0 = a22 * q; + + for (p = pmin; p <= pmax; p++) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = tfloor(u_); + v_ = v_0 + a21 * p; + v = tfloor(v_); + if ((UINT)u < mu && (UINT)v < mv) + break; + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_32 = bufin_32 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) : BORDER) + + fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? grey(in_32[du + dv]) : BORDER) + + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? grey(in_32[0]) : BORDER) + + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) : BORDER)); + } + p1 = p; + for (p = pmax; p > p1; p--) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = tfloor(u_); + v_ = v_0 + a21 * p; + v = tfloor(v_); + if ((UINT)u < mu && (UINT)v < mv) + break; + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_32 = bufin_32 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) : BORDER) + + fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? grey(in_32[du + dv]) : BORDER) + + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? grey(in_32[0]) : BORDER) + + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) : BORDER)); + } + p2 = p; + for (p = p1; p <= p2; p++) + if (!prow[p]) { + u_ = u_0 + a11 * p; + u = (int)(u_); + v_ = v_0 + a21 * p; + v = (int)(v_); + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + in_32 = bufin_32 + (u * du + v * dv); + prow[p] = (float)troundp(fu * gv * grey(in_32[du]) + fu * fv * grey(in_32[du + dv]) + + gu * gv * grey(in_32[0]) + gu * fv * grey(in_32[dv])); + } +} + +//--------------------------------------------------------------------------- +typedef float *MyFloatPtr; + +static void rop_resample_gr8(const TRasterGR8P &rin, TRasterGR8P rout, + const TAffine &aff, const TAffine &invrot, + FILTER *rowflt, int pmin, int pmax, + FILTER *colflt, int qmin, int qmax, int nrows, + int flatradu, int flatradv, + double flatradx_, double flatrady_, + NOCALC *rownoc, int nocdiamx, + NOCALC *colnoc, int nocdiamy) +{ + + FILTER *xflt, *yflt; + UCHAR *bufin_gr8, *bufout_gr8, *in_gr8, *out_gr8; + float *prow_base, *prow, **xrow_base, **xrow, *xxx, tmp; + double x_, y_; + int u, v; //, vw; + int p, q; + int x, y; + int lu, lv, mu, mv; + int lx, ly, mx, my; + //int dudp, dudq, dvdp, dvdq; + int topq, topy; + int wrapin, wrapout; + int flatdiamu, flatdiamv; + int xlo, xhi, ylo, yhi; + int *nocheight; + int nocwidth; + int i, j; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + bufin_gr8 = (UCHAR *)rin->pixels(); + bufout_gr8 = (UCHAR *)rout->pixels(); + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + lu = rin->getLx(); + mu = lu - 1; + lv = rin->getLy(); + mv = lv - 1; + lx = rout->getLx(); + mx = lx - 1; + ly = rout->getLy(); + my = ly - 1; + prow_base = new float[pmax - pmin + 1]; + prow = prow_base - pmin; + xrow_base = new MyFloatPtr[qmax - (qmin - nrows) + 1]; + xrow = xrow_base - (qmin - nrows); + topq = qmin; + + //app = xrow+topq-nrows; + i = 0; + j = 3; + + for (i = 0; i < nrows; i++) + *(xrow + topq - nrows + i) = new float[lx]; + + //while(app= flatdiamv) + if (colval[u] == flatval) + flatcols++; + else + flatcols = 1; + else + flatcols = 0; + flatval = colval[u]; + if (flatcols >= flatdiamu) { +#ifdef VECCHIA_MANIERA + x_ = AFF_M_V_1(aff, u - flatradu, v - flatradv); + y_ = AFF_M_V_2(aff, u - flatradu, v - flatradv); + xlo = CEIL(x_ - flatradx_); + xhi = FLOOR(x_ + flatradx_); + ylo = CEIL(y_ - flatrady_); + yhi = FLOOR(y_ + flatrady_); + NOT_LESS_THAN(0, xlo); + NOT_MORE_THAN(mx, xhi); + NOT_LESS_THAN(0, ylo); + NOT_MORE_THAN(my, yhi); +#endif + xlo = tmax(0, (int)xlo_); + xhi = tmin(mx, (int)xhi_); + ylo = tmax(0, (int)ylo_); + yhi = tmin(my, (int)yhi_); + for (y = ylo; y <= yhi; y++) + for (x = xlo; x <= xhi; x++) + bufout_gr8[x + y * wrapout] = flatval, count++; + } + xlo_ += aff.a11; + xhi_ += aff.a11; + ylo_ += aff.a21; + yhi_ += aff.a21; + } + } + delete colval; + delete colheight; + + topy = 0; + /*TCALLOC (nocheight, lx);*/ + nocheight = new int[lx]; + memset(nocheight, 0, lx * sizeof(int)); + out_gr8 = bufout_gr8; + for (x = 0; x < lx; x++) + if (out_gr8[x] != GREY_GR8) + nocheight[x]++; + else + nocheight[x] = 0; + + for (y = 0, yflt = colflt; y < ly; y++, yflt++) { + for (; topq <= yflt->last; topq++) { + xrow[topq] = xrow[topq - nrows]; + xxx = xrow[topq]; + memset(xxx, 0, sizeof(*xxx) * lx); /* 0.0 == nocalc */ + while (topy < ly - 1 && colnoc[topy].last < topq) { + topy++; + out_gr8 = bufout_gr8 + topy * wrapout; + for (x = 0; x < lx; x++) + if (out_gr8[x] != GREY_GR8) + nocheight[x]++; + else + nocheight[x] = 0; + } + if (topy < ly && colnoc[topy].first <= topq) { + for (x = 0; x < lx; x++) + if (nocheight[x] < nocdiamy) + xxx[x] = 1.0; /* 1.0 == calc */ + } else { + for (x = 0; x < lx; x++) + xxx[x] = 1.0; /* 1.0 == calc */ + } + + memset(prow + pmin, 0, sizeof(*prow) * (pmax - pmin + 1)); /* 0.0 == calc */ + nocwidth = 0; + for (x = 0; x < lx; x++) + if (xxx[x]) + nocwidth = 0; + else { + nocwidth++; + if (nocwidth >= nocdiamx) + for (p = rownoc[x].first; p <= rownoc[x].last; p++) + prow[p] = 1.0; /* 1.0 == nocalc */ + } + get_prow_gr8(rin, invrot.a11, invrot.a12, invrot.a21, invrot.a22, + pmin, pmax, topq, prow); + for (x = 0, xflt = rowflt; x < lx; x++, xflt++) + if (xxx[x]) { + for (tmp = 0.0, p = xflt->first; p <= xflt->last; p++) + tmp += xflt->w[p] * prow[p]; + xxx[x] = tmp; + } + } + out_gr8 = bufout_gr8 + wrapout * y; + for (x = 0; x < lx; x++) + if (out_gr8[x] == GREY_GR8) { + for (tmp = 0.0, q = yflt->first; q <= yflt->last; q++) + tmp += yflt->w[q] * xrow[q][x]; + out_gr8[x] = TO8BIT(tmp); + } + } + //cest_plus_facile (xrow); + + for (q = 0; q < nrows; q++) + delete xrow_base[q]; + delete xrow_base; + delete prow_base; +} + +//--------------------------------------------------------------------------- + +static void rop_resample_rgbm32_gr8(const TRaster32P &rin, TRasterGR8P rout, + const TAffine &aff, const TAffine &invrot, + FILTER *rowflt, int pmin, int pmax, + FILTER *colflt, int qmin, int qmax, int nrows, + int flatradu, int flatradv, + double flatradx_, double flatrady_, + NOCALC *rownoc, int nocdiamx, + NOCALC *colnoc, int nocdiamy) +{ + + FILTER *xflt, *yflt; + UCHAR *bufout_gr8, *out_gr8; + TPixel *bufin_32, *in_32; + float *prow_base, *prow, **xrow_base, **xrow, *xxx, tmp; + double x_, y_; + int u, v; //, vw; + int p, q; + int x, y; + int lu, lv, mu, mv; + int lx, ly, mx, my; + //int dudp, dudq, dvdp, dvdq; + int topq, topy; + int wrapin, wrapout; + int flatdiamu, flatdiamv; + int xlo, xhi, ylo, yhi; + int *nocheight; + int nocwidth; + int i, j; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + bufin_32 = (TPixel *)rin->pixels(); + bufout_gr8 = (UCHAR *)rout->pixels(); + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + + lu = rin->getLx(); + mu = lu - 1; + lv = rin->getLy(); + mv = lv - 1; + lx = rout->getLx(); + mx = lx - 1; + ly = rout->getLy(); + my = ly - 1; + prow_base = new float[pmax - pmin + 1]; + prow = prow_base - pmin; + xrow_base = new MyFloatPtr[qmax - (qmin - nrows) + 1]; + xrow = xrow_base - (qmin - nrows); + topq = qmin; + + //app = xrow+topq-nrows; + i = 0; + j = 3; + + for (i = 0; i < nrows; i++) + *(xrow + topq - nrows + i) = new float[lx]; + + //while(app= flatdiamv) + if (colval[u] == flatval) + flatcols++; + else + flatcols = 1; + else + flatcols = 0; + flatval = colval[u]; + if (flatcols >= flatdiamu) { +#ifdef VECCHIA_MANIERA + x_ = AFF_M_V_1(aff, u - flatradu, v - flatradv); + y_ = AFF_M_V_2(aff, u - flatradu, v - flatradv); + xlo = CEIL(x_ - flatradx_); + xhi = FLOOR(x_ + flatradx_); + ylo = CEIL(y_ - flatrady_); + yhi = FLOOR(y_ + flatrady_); + NOT_LESS_THAN(0, xlo); + NOT_MORE_THAN(mx, xhi); + NOT_LESS_THAN(0, ylo); + NOT_MORE_THAN(my, yhi); +#endif + xlo = tmax(0, (int)xlo_); + xhi = tmin(mx, (int)xhi_); + ylo = tmax(0, (int)ylo_); + yhi = tmin(my, (int)yhi_); + for (y = ylo; y <= yhi; y++) + for (x = xlo; x <= xhi; x++) + bufout_gr8[x + y * wrapout] = flatval, count++; + } + xlo_ += aff.a11; + xhi_ += aff.a11; + ylo_ += aff.a21; + yhi_ += aff.a21; + } + } + delete colval; + delete colheight; + + topy = 0; + /*TCALLOC (nocheight, lx);*/ + nocheight = new int[lx]; + memset(nocheight, 0, lx * sizeof(int)); + out_gr8 = bufout_gr8; + for (x = 0; x < lx; x++) + if (out_gr8[x] != GREY_GR8) + nocheight[x]++; + else + nocheight[x] = 0; + + for (y = 0, yflt = colflt; y < ly; y++, yflt++) { + for (; topq <= yflt->last; topq++) { + xrow[topq] = xrow[topq - nrows]; + xxx = xrow[topq]; + memset(xxx, 0, sizeof(*xxx) * lx); /* 0.0 == nocalc */ + while (topy < ly - 1 && colnoc[topy].last < topq) { + topy++; + out_gr8 = bufout_gr8 + topy * wrapout; + for (x = 0; x < lx; x++) + if (out_gr8[x] != GREY_GR8) + nocheight[x]++; + else + nocheight[x] = 0; + } + if (topy < ly && colnoc[topy].first <= topq) { + for (x = 0; x < lx; x++) + if (nocheight[x] < nocdiamy) + xxx[x] = 1.0; /* 1.0 == calc */ + } else { + for (x = 0; x < lx; x++) + xxx[x] = 1.0; /* 1.0 == calc */ + } + + memset(prow + pmin, 0, sizeof(*prow) * (pmax - pmin + 1)); /* 0.0 == calc */ + nocwidth = 0; + for (x = 0; x < lx; x++) + if (xxx[x]) + nocwidth = 0; + else { + nocwidth++; + if (nocwidth >= nocdiamx) + for (p = rownoc[x].first; p <= rownoc[x].last; p++) + prow[p] = 1.0; /* 1.0 == nocalc */ + } + get_prow_gr8(rin, invrot.a11, invrot.a12, invrot.a21, invrot.a22, + pmin, pmax, topq, prow); + for (x = 0, xflt = rowflt; x < lx; x++, xflt++) + if (xxx[x]) { + for (tmp = 0.0, p = xflt->first; p <= xflt->last; p++) + tmp += xflt->w[p] * prow[p]; + xxx[x] = tmp; + } + } + out_gr8 = bufout_gr8 + wrapout * y; + for (x = 0; x < lx; x++) + if (out_gr8[x] == GREY_GR8) { + for (tmp = 0.0, q = yflt->first; q <= yflt->last; q++) + tmp += yflt->w[q] * xrow[q][x]; + out_gr8[x] = TO8BIT(tmp); + } + } + //cest_plus_facile (xrow); + + for (q = 0; q < nrows; q++) + delete xrow_base[q]; + delete xrow_base; + delete prow_base; +} + +//--------------------------------------------------------------------------- + +// #define USE_STATIC_VARS + +//--------------------------------------------------------------------------- + +template +void rop_resample_rgbm(TRasterPT rout, const TRasterPT &rin, + const TAffine &aff, TRop::ResampleFilterType flt_type, double blur) +{ +#define FILTER_RESOLUTION 1024 +#define MAX_FILTER_VAL 32767 + +#ifdef USE_STATIC_VARS + static TRop::ResampleFilterType current_flt_type = TRop::None; + static short *filter_array = 0; + static short *filter = 0; + static int min_filter_fg, max_filter_fg; + static int filter_array_size = 0; + static int n_pix = 0; + static int *pix_ref_u = 0; + static int *pix_ref_v = 0; + static int *pix_ref_f = 0; + static int *pix_ref_g = 0; + static int current_max_n_pix = 0; +#else + short *filter_array = 0; + short *filter = 0; + int min_filter_fg, max_filter_fg; + int filter_array_size = 0; + int n_pix = 0; + int *pix_ref_u = 0; + int *pix_ref_v = 0; + int *pix_ref_f = 0; + int *pix_ref_g = 0; + int current_max_n_pix = 0; +#endif + int filter_st_radius; + int filter_fg_radius; + int filter_size; + int f; + double s_; + double weight_; + int weight; + TAffine aff_uv2xy; + TAffine aff_xy2uv; + TAffine aff0_uv2xy; + TAffine aff0_xy2st; + TAffine aff0_uv2st; + TAffine aff0_st2fg; + TAffine aff0_uv2fg; + TAffine aff0_fg2uv; + double scale_x, scale_y; + double inv_blur; + int max_n_pix; + double min_pix_out_u_, min_pix_out_v_; + double max_pix_out_u_, max_pix_out_v_; + int min_pix_ref_u, min_pix_ref_v; + int max_pix_ref_u, max_pix_ref_v; + int cur_pix_ref_u, cur_pix_ref_v; + double cur_pix_ref_f_, cur_pix_ref_g_; + int cur_pix_ref_f, cur_pix_ref_g; + double min_ref_out_f_, min_ref_out_g_; + double max_ref_out_f_, max_ref_out_g_; + int min_ref_out_f, min_ref_out_g; + int max_ref_out_f, max_ref_out_g; + int min_pix_ref_f, min_pix_ref_g; + int max_pix_ref_f, max_pix_ref_g; + int min_pix_out_f, min_pix_out_g; + int max_pix_out_f, max_pix_out_g; + int min_pix_out_fg; + int max_pix_out_fg; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + assert(flt_type != TRop::None); + + //Retrieve the filter radius in the st and fx references + filter_st_radius = get_filter_radius(flt_type); + filter_fg_radius = filter_st_radius * FILTER_RESOLUTION; + + //Retrieve the transformation affines among the involved references + //NOTE: The 0.5 translation is needed in order to make the later + //resample_main procedures work with pixel centers. + aff_uv2xy = aff * TTranslation(0.5, 0.5); + aff0_uv2xy = aff_uv2xy.place(0.0, 0.0, 0.0, 0.0); + aff_xy2uv = aff_uv2xy.inv(); + + //Consider the norm of (1,0) and (0,1) images. + scale_x = sqrt(sq(aff_uv2xy.a11) + sq(aff_uv2xy.a12)); + scale_y = sqrt(sq(aff_uv2xy.a21) + sq(aff_uv2xy.a22)); + + //Inserting the following scale will make shrinks look smooth. + aff0_xy2st = TScale((scale_x > 1.0) ? 1.0 / scale_x : 1.0, + (scale_y > 1.0) ? 1.0 / scale_y : 1.0); + + if (blur > 1.0) //Consider the blur as a scale in the filter reference + { + inv_blur = 1.0 / blur; + aff0_xy2st = TScale(inv_blur, inv_blur) * aff0_xy2st; + } + + aff0_uv2st = aff0_xy2st * aff0_uv2xy; + aff0_st2fg = TScale(FILTER_RESOLUTION, FILTER_RESOLUTION); + aff0_uv2fg = aff0_st2fg * aff0_uv2st; + aff0_fg2uv = aff0_uv2fg.inv(); + + //Take the pre-image of the filter mask in uv coordinates. This is where + //input pixels will be taken to find an output one. + minmax(-filter_fg_radius, -filter_fg_radius, + filter_fg_radius, filter_fg_radius, + aff0_fg2uv, + min_pix_out_u_, min_pix_out_v_, + max_pix_out_u_, max_pix_out_v_); + + //Adjust them to integer coordinates. The intent here is that + //of isolating their fractionary part - furthermore, we'll take + //the *opposites* of fractionary parts (explained later). + //NOTE: We'll assume we want to include in the filter mask all + //*integer positions around a fractionary displacement of the origin*; + //so the approximations below are stricly necessary. + + min_pix_ref_u = intLE(min_pix_out_u_); + min_pix_ref_v = intLE(min_pix_out_v_); + max_pix_ref_u = intGE(max_pix_out_u_); + max_pix_ref_v = intGE(max_pix_out_v_); + + if (blur <= 1.0) { + //If the blur radius has sub-pixel width + if (aff_uv2xy.a12 == 0.0 && aff_uv2xy.a21 == 0.0) { + //And it's the sole scales case + if (aff_uv2xy.a11 == 1.0 && isInt(aff_uv2xy.a13 - 0.5)) { + //And the x mapping is bijective, then prevent any filtering. + min_pix_ref_u = 0; + max_pix_ref_u = 0; + } + if (aff_uv2xy.a22 == 1.0 && isInt(aff_uv2xy.a23 - 0.5)) { + //And the y mapping is bijective ... + min_pix_ref_v = 0; + max_pix_ref_v = 0; + } + } else if (aff_uv2xy.a11 == 0.0 && aff_uv2xy.a22 == 0.0) { + //The mirrored version of the one above + if (aff_uv2xy.a12 == 1.0 && isInt(aff_uv2xy.a13 - 0.5)) { + min_pix_ref_v = 0; + max_pix_ref_v = 0; + } + if (aff_uv2xy.a21 == 1.0 && isInt(aff_uv2xy.a23 - 0.5)) { + min_pix_ref_u = 0; + max_pix_ref_u = 0; + } + } + } + + //Take the number of pixels involved in the filter (uv reference) + max_n_pix = (max_pix_ref_u - min_pix_ref_u + 1) * + (max_pix_ref_v - min_pix_ref_v + 1); + + if (max_n_pix > current_max_n_pix) { + current_max_n_pix = max_n_pix; + if (pix_ref_u) + delete[] pix_ref_u; + pix_ref_u = new int[current_max_n_pix]; + if (pix_ref_v) + delete[] pix_ref_v; + pix_ref_v = new int[current_max_n_pix]; + if (pix_ref_f) + delete[] pix_ref_f; //These will provide the images of the formers + pix_ref_f = new int[current_max_n_pix]; + if (pix_ref_g) + delete[] pix_ref_g; + pix_ref_g = new int[current_max_n_pix]; + assert(pix_ref_u && pix_ref_v && pix_ref_f && pix_ref_g); + } + + //Build the image of fractionary domain from the uv to fg reference + minmax(-1, -1, 0, 0, + aff0_uv2fg, + min_ref_out_f_, min_ref_out_g_, + max_ref_out_f_, max_ref_out_g_); + + min_ref_out_f = tround(min_ref_out_f_); + min_ref_out_g = tround(min_ref_out_g_); + max_ref_out_f = tround(max_ref_out_f_); + max_ref_out_g = tround(max_ref_out_g_); + + //Remember that negative fractionary parts must be subtracted from their integer counterparts + min_pix_ref_f = -filter_fg_radius - max_ref_out_f; + min_pix_ref_g = -filter_fg_radius - max_ref_out_g; + max_pix_ref_f = filter_fg_radius - min_ref_out_f; + max_pix_ref_g = filter_fg_radius - min_ref_out_g; + + min_pix_out_f = c_maxint; + min_pix_out_g = c_maxint; + max_pix_out_f = c_minint; + max_pix_out_g = c_minint; + n_pix = 0; + + if (!pix_ref_u || !pix_ref_v || !pix_ref_f || !pix_ref_g) { +#ifndef USE_STATIC_VARS + if (pix_ref_u) + delete[] pix_ref_u; + if (pix_ref_v) + delete[] pix_ref_v; + if (pix_ref_f) + delete[] pix_ref_f; + if (pix_ref_g) + delete[] pix_ref_g; +#endif + throw TRopException("tresample.cpp line2640 function rop_resample_rgbm() : alloc pix_ref failed"); + } + + //Build the *integer* part of the fg images of those coordinates inside the uv filter bounds. + + //NOTE: Doing so reduces the execution time for the later resample_main procedure - + //the idea is the following: + // We want to build the output pixel (x,y) obtained from the source image through A. + // Then, we find (u,v) = (A^-1) * (x,y) = ([u],[v]) + ({u},{v}), where [] and {} + // denote integer and fractionary parts. + // Now, the convolution positions on fg for (u,v) can be thought of being calculated by taking + // images of integer displacements of (u,v). So, their calculation is definitely *not* directly + // dependent on the fractionary part of (u,v) - that is, the (i,j)th displacement position of FG(u,v) + // is: + // FG([u]+i,[v]+j) = FG(u+i,v+j) - FG({u},{v}) = FG(i,j) - FG({u},{v}); + // + // where it is assumed that FG(u,v) = (0,0), since the filter is to be considered centered on (u,v). + + for (cur_pix_ref_v = min_pix_ref_v; + cur_pix_ref_v <= max_pix_ref_v; + cur_pix_ref_v++) + for (cur_pix_ref_u = min_pix_ref_u; + cur_pix_ref_u <= max_pix_ref_u; + cur_pix_ref_u++) { + //Get the image of current uv position + cur_pix_ref_f_ = affMV1(aff0_uv2fg, cur_pix_ref_u, cur_pix_ref_v); + cur_pix_ref_g_ = affMV2(aff0_uv2fg, cur_pix_ref_u, cur_pix_ref_v); + + //And round it to the closest integer in fg + cur_pix_ref_f = tround(cur_pix_ref_f_); + cur_pix_ref_g = tround(cur_pix_ref_g_); + if (min_pix_ref_f <= cur_pix_ref_f && cur_pix_ref_f <= max_pix_ref_f && + min_pix_ref_g <= cur_pix_ref_g && cur_pix_ref_g <= max_pix_ref_g) { + pix_ref_u[n_pix] = cur_pix_ref_u; + pix_ref_v[n_pix] = cur_pix_ref_v; + pix_ref_f[n_pix] = cur_pix_ref_f; + pix_ref_g[n_pix] = cur_pix_ref_g; + notMoreThan(cur_pix_ref_f + min_ref_out_f, min_pix_out_f); //cur_pix_ref > min_pix_out - min_ref_out + notMoreThan(cur_pix_ref_g + min_ref_out_g, min_pix_out_g); + notLessThan(cur_pix_ref_f + max_ref_out_f, max_pix_out_f); //cur_pix_ref < max_pix_out - max_ref_out + notLessThan(cur_pix_ref_g + max_ref_out_g, max_pix_out_g); + n_pix++; + } + } + assert(n_pix > 0); + +#ifdef USE_STATIC_VARS + if (flt_type != current_flt_type) { + current_flt_type = flt_type; +#endif + + //Build a sufficient filter weights array + min_filter_fg = -filter_fg_radius - FILTER_RESOLUTION * 3 / 2; //??? + max_filter_fg = filter_fg_radius + FILTER_RESOLUTION * 3 / 2; + filter_size = max_filter_fg - min_filter_fg + 1; + if (filter_size > filter_array_size) //For the static vars case... + { + if (filter_array) + delete[] filter_array; + filter_array = new short[filter_size]; + assert(filter_array); + filter_array_size = filter_size; + } + filter = filter_array - min_filter_fg; //Take the position corresponding to fg's (0,0) in the array + filter[0] = MAX_FILTER_VAL; + for (f = 1, s_ = 1.0 / FILTER_RESOLUTION; + f < filter_fg_radius; + f++, s_ += 1.0 / FILTER_RESOLUTION) { + //Symmetrically build the array + weight_ = get_filter_value(flt_type, s_) * (double)MAX_FILTER_VAL; + weight = tround(weight_); + filter[f] = weight; + filter[-f] = weight; + } + for (f = filter_fg_radius; f <= max_filter_fg; f++) + filter[f] = 0; + for (f = -filter_fg_radius; f >= min_filter_fg; f--) + filter[f] = 0; + +#ifdef USE_STATIC_VARS + } +#endif + + //Considering the bounding square in fg + min_pix_out_fg = tmin(min_pix_out_f, min_pix_out_g); + max_pix_out_fg = tmax(max_pix_out_f, max_pix_out_g); + if (min_pix_out_fg < min_filter_fg || max_pix_out_fg > max_filter_fg) { + //Reallocate the filter... and so on... + filter_size = max_pix_out_fg - min_pix_out_fg + 1; + if (filter_size > filter_array_size) { + //controllare!! + //TREALLOC (filter_array, filter_size) + if (filter_array) + delete[] filter_array; + filter_array = new short[filter_size]; + + assert(filter_array); + filter_array_size = filter_size; + } + filter = filter_array - min_filter_fg; + if (min_pix_out_fg < min_filter_fg) { + int delta = min_filter_fg - min_pix_out_fg; + + for (f = max_filter_fg; f >= min_filter_fg; f--) + filter[f + delta] = filter[f]; + filter += delta; + for (f = min_filter_fg - 1; f >= min_pix_out_fg; f--) + filter[f] = 0; + min_filter_fg = min_pix_out_fg; + } + if (max_pix_out_fg > max_filter_fg) { + for (f = max_filter_fg + 1; f <= max_pix_out_fg; f++) + filter[f] = 0; + max_filter_fg = max_pix_out_fg; + } + } + +#ifdef WIN32 + if ((TSystem::getCPUExtensions() & TSystem::CpuSupportsSse2) && T::maxChannelValue == 255) + resample_main_rgbm_SSE2(rout, rin, aff_xy2uv, aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + else +#endif + if (n_pix >= 512 || T::maxChannelValue > 255) + resample_main_rgbm( + rout, rin, aff_xy2uv, aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + else + resample_main_rgbm( + rout, rin, aff_xy2uv, aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + +#ifndef USE_STATIC_VARS + if (filter_array) + delete[] filter_array; + if (pix_ref_u) + delete[] pix_ref_u; + if (pix_ref_v) + delete[] pix_ref_v; + if (pix_ref_f) + delete[] pix_ref_f; + if (pix_ref_g) + delete[] pix_ref_g; +#endif + + ///////////////////////////////////////////////////////// + // INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + /* +switch (RASRAS (rin->type, rout->type)) + { + case RASRAS (RAS_RGB_, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGBM): + resample_main_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_RGBM, RAS_RGBM64): + resample_main_rgbm_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_RGBM64, RAS_RGBM64): + resample_main_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM16, RAS_RGBM): + resample_main_cm16_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM24, RAS_RGBM): + resample_main_cm24_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM16, RAS_RGBM64): + resample_main_cm16_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM24, RAS_RGBM64): + resample_main_cm24_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + DEFAULT: + assert ( !"bad raster type combination"); + } +*/ + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// +} + +//--------------------------------------------------------------------------- + +static void free_filter(FILTER *filter, int lx) +{ + for (--lx; lx >= 0; lx--) + if (filter[lx].w_base) + delete (filter[lx].w_base); + delete (filter); +} + +//----------------------------------------------------------------------------- + +void do_resample(TRasterGR8P rout, const TRasterGR8P &rin, + const TAffine &aff, TRop::ResampleFilterType flt_type, double blur) +{ + double jacob; + double s11, s22, s13, s23; + FILTER *rowf, *colf; + NOCALC *rown, *coln; + int pmin, pmax, qmin, qmax; + int nrows, dummy; + double negradu_, negradv_, posradu_, posradv_; + double negradx_, negrady_, posradx_, posrady_; + int nocdiamx, nocdiamy; + double rad_x, rad_y; + TAffine rot, scale, invrot; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) /* immagine out vuota */ + { + return; + } + if (!(rin->getLx() > 0 && rin->getLy() > 0)) /* immagine in vuota */ + { + rout->fill(TPixelGR8::Black); //Black_rgbm + return; + } + + TRasterGR8P routGR8 = rout, rinGR8 = rin; + if (routGR8 && rinGR8) { + jacob = fabs(aff.det()); + if (jacob == 0.0) + throw TRopException("AFFINE transformation has zero determinant"); + if (jacob < 1E-30) + throw TRopException("AFFINE transformation has (nearly) zero determinant"); + s11 = sqrt(jacob); /* provvisorio */ + s22 = s11; + s13 = aff.a13; + s23 = aff.a23; + + //rot = aff_place (0.0, 0.0, 0.0, 0.0, TScale(1/s11, 1/s22)*aff);//eventualmente invertire ordine + + rot = (TScale(1 / s11, 1 / s22) * aff).place(0.0, 0.0, 0.0, 0.0); + + //scale = aff_place (0.0, 0.0, s13, s23, TScale(s11, s22)); + scale = TScale(s11, s22).place(0.0, 0.0, s13, s23); + invrot = rot.inv(); + + rowf = create_filter(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + rad_x, pmin, pmax, dummy); + colf = create_filter(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + rad_y, qmin, qmax, nrows); + rown = create_nocalc(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + pmin, pmax, nocdiamx); + coln = create_nocalc(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + qmin, qmax, nocdiamy); + +#ifdef DBMALLOC + malloc_chain_check(TRUE); +#endif +#ifdef MEMLEAK + CheckMemory(); +#endif + + TAffine aff_0 = aff.place(0.0, 0.0, 0.0, 0.0); + TAffine inv_0 = aff_0.inv(); + + minmax(-0.5, -0.5, 0.5, 0.5, aff_0, + negradx_, negrady_, posradx_, posrady_); + double flatradx_ = posradx_; + double flatrady_ = posrady_; + minmax(negradx_ - rad_x, negrady_ - rad_y, posradx_ + rad_x, posrady_ + rad_y, inv_0, + negradu_, negradv_, posradu_, posradv_); + + int flatradu = tceil(posradu_) - 1; + int flatradv = tceil(posradv_) - 1; + + rop_resample_gr8(rin, rout, aff, invrot, + rowf, pmin, pmax, colf, qmin, qmax, nrows, + flatradu, flatradv, flatradx_, flatrady_, + rown, nocdiamx, coln, nocdiamy); + + //free_nocalc (coln); + if (coln) + delete (coln); + //free_nocalc (rown); + if (rown) + delete (rown); + free_filter(colf, rout->getLy()); + free_filter(rowf, rout->getLx()); + //----NON GESTIAMO ANCORA EXTRA BUFFER + //rop_resample_extra (rin, rout, aff); + + return; + } else + throw TRopException("unsupported pixel type"); +} + +//----------------------------------------------------------------------------- + +void do_resample(TRasterGR8P rout, const TRaster32P &rin, + const TAffine &aff, TRop::ResampleFilterType flt_type, double blur) +{ + double jacob; + double s11, s22, s13, s23; + FILTER *rowf, *colf; + NOCALC *rown, *coln; + int pmin, pmax, qmin, qmax; + int nrows, dummy; + double negradu_, negradv_, posradu_, posradv_; + double negradx_, negrady_, posradx_, posrady_; + int nocdiamx, nocdiamy; + double rad_x, rad_y; + TAffine rot, scale, invrot; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) /* immagine out vuota */ + { + return; + } + if (!(rin->getLx() > 0 && rin->getLy() > 0)) /* immagine in vuota */ + { + rout->fill(TPixelGR8::Black); //Black_rgbm + return; + } + + jacob = fabs(aff.det()); + if (jacob == 0.0) + throw TRopException("AFFINE transformation has zero determinant"); + if (jacob < 1E-30) + throw TRopException("AFFINE transformation has (nearly) zero determinant"); + s11 = sqrt(jacob); /* provvisorio */ + s22 = s11; + s13 = aff.a13; + s23 = aff.a23; + + //rot = aff_place (0.0, 0.0, 0.0, 0.0, TScale(1/s11, 1/s22)*aff);//eventualmente invertire ordine + + rot = (TScale(1 / s11, 1 / s22) * aff).place(0.0, 0.0, 0.0, 0.0); + + //scale = aff_place (0.0, 0.0, s13, s23, TScale(s11, s22)); + scale = TScale(s11, s22).place(0.0, 0.0, s13, s23); + invrot = rot.inv(); + + rowf = create_filter(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + rad_x, pmin, pmax, dummy); + colf = create_filter(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + rad_y, qmin, qmax, nrows); + rown = create_nocalc(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + pmin, pmax, nocdiamx); + coln = create_nocalc(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + qmin, qmax, nocdiamy); + +#ifdef DBMALLOC + malloc_chain_check(TRUE); +#endif +#ifdef MEMLEAK + CheckMemory(); +#endif + + TAffine aff_0 = aff.place(0.0, 0.0, 0.0, 0.0); + TAffine inv_0 = aff_0.inv(); + + minmax(-0.5, -0.5, 0.5, 0.5, aff_0, + negradx_, negrady_, posradx_, posrady_); + double flatradx_ = posradx_; + double flatrady_ = posrady_; + minmax(negradx_ - rad_x, negrady_ - rad_y, posradx_ + rad_x, posrady_ + rad_y, inv_0, + negradu_, negradv_, posradu_, posradv_); + + int flatradu = tceil(posradu_) - 1; + int flatradv = tceil(posradv_) - 1; + + rop_resample_rgbm32_gr8(rin, rout, aff, invrot, + rowf, pmin, pmax, colf, qmin, qmax, nrows, + flatradu, flatradv, flatradx_, flatrady_, + rown, nocdiamx, coln, nocdiamy); + + //free_nocalc (coln); + if (coln) + delete (coln); + //free_nocalc (rown); + if (rown) + delete (rown); + free_filter(colf, rout->getLy()); + free_filter(rowf, rout->getLx()); + //----NON GESTIAMO ANCORA EXTRA BUFFER + //rop_resample_extra (rin, rout, aff); + + return; + + // else throw TRopException("unsupported pixel type"); +} + +//----------------------------------------------------------------------------- + +template +void do_resample(TRasterPT rout, const TRasterPT &rin, + const TAffine &aff, TRop::ResampleFilterType flt_type, double blur) + +{ +/* +TAffine scale; +TAffine rot; +TAffine invrot; +TAffine aff_0, inv_0; +*/ +#ifdef ALTRI_TIPI_DI_RASTER + double jacob; + double s11, s22, s13, s23; + FILTER *rowf, *colf; + NOCALC *rown, *coln; + int pmin, pmax, qmin, qmax; + int nrows, dummy; + double negradu_, negradv_, posradu_, posradv_; + double negradx_, negrady_, posradx_, posrady_; + int nocdiamx, nocdiamy; + double rad_x, rad_y; + +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) /* immagine out vuota */ + { + return; + } + if (!(rin->getLx() > 0 && rin->getLy() > 0)) /* immagine in vuota */ + { + rout->fill(T::Black); //Black_rgbm + + ///////////////////////////////////////////////////////// + // INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + /* switch (rout->type) + { + + case RAS_GR8: + for (y = 0; y < rout->ly; y++) + for (x = 0; x < rout->lx; x++) + ((UCHAR*)rout->buffer)[x + y * rout->wrap] = BLACK_GR8; + CASE RAS_RGB_: + __OR RAS_RGBM: + for (y = 0; y < rout->ly; y++) + for (x = 0; x < rout->lx; x++) + ((LPIXEL*)rout->buffer)[x + y * rout->wrap] = Black_rgbm; + CASE RAS_RGBM64: + for (y = 0; y < rout->ly; y++) + for (x = 0; x < rout->lx; x++) + ((SPIXEL*)rout->buffer)[x + y * rout->wrap] = Black_rgbm64; + DEFAULT: + abort(); + } + */ + //---- NON GESTIAMO ANCORA EXTRA BUFFER + //ZERO_EXTRA_OF (rout) + + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + return; + } + + TRasterPT rout_ = rout, rin_ = rin; + if (rout_ && rin_) { + rop_resample_rgbm(rout, rin, aff, flt_type, blur); + return; + } else + throw TRopException("unsupported pixel type"); + +#ifdef ALTRI_TIPI_DI_RASTER + ///////////////////////////////////////////////////////// + // INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + /* + switch (RASRAS (rin->type, rout->type)) + { + case RASRAS (RAS_CM16, RAS_RGB_): + __OR RASRAS (RAS_CM24, RAS_RGB_): + __OR RASRAS (RAS_CM16, RAS_RGBM): + __OR RASRAS (RAS_CM24, RAS_RGBM): + __OR RASRAS (RAS_CM16, RAS_RGBM64): + __OR RASRAS (RAS_CM24, RAS_RGBM64): + __OR RASRAS (RAS_RGB_, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGBM): + __OR RASRAS (RAS_RGBM, RAS_RGBM64): + __OR RASRAS (RAS_RGBM64, RAS_RGBM64): + + rop_resample_rgbm (rin, rout, aff, flt_type, 1.0); + + #ifdef CHECK_IF_PREMULTIPLIED + { + int x, y; + LPIXEL *buf, pix; + + buf = rout->buffer; + for (y = 0; y < rout->getLy(); y++) + for (x = 0; x < rout->getLx(); x++) + { + pix = buf[x + y * rout->wrap]; + if (pix.m < pix.b || pix.m < pix.g || pix.m < pix.r) + printf ("(%d,%d): 0x%02x 0x%02x 0x%02x 0x%02x\n", + x, y, pix.m, pix.b, pix.g, pix.r); + } + } + #endif +//----NON GESTIAMO ANCORA EXTRA BUFFER + rop_resample_extra (rin, rout, aff); + return; + } +*/ + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + jacob = fabs(aff.det()); + if (jacob == 0.0) + throw TRopException("AFFINE transformation has zero determinant"); + if (jacob < 1E-30) + throw TRopException("AFFINE transformation has (nearly) zero determinant"); + s11 = sqrt(jacob); /* provvisorio */ + s22 = s11; + s13 = aff.a13; + s23 = aff.a23; + + //rot = aff_place (0.0, 0.0, 0.0, 0.0, TScale(1/s11, 1/s22)*aff);//eventualmente invertire ordine + rot = (TScale(1 / s11, 1 / s22) * aff).place(0.0, 0.0, 0.0, 0.0); + + //scale = aff_place (0.0, 0.0, s13, s23, TScale(s11, s22)); + scale = TScale(s11, s22).place(0.0, 0.0, s13, s23); + invrot = rot.inv(); + + rowf = create_filter(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + rad_x, pmin, pmax, dummy); + colf = create_filter(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + rad_y, qmin, qmax, nrows); + rown = create_nocalc(flt_type, blur, scale.a11, scale.a13, rout->getLx(), + pmin, pmax, nocdiamx); + coln = create_nocalc(flt_type, blur, scale.a22, scale.a23, rout->getLy(), + qmin, qmax, nocdiamy); + +#ifdef DBMALLOC + malloc_chain_check(TRUE); +#endif +#ifdef MEMLEAK + CheckMemory(); +#endif + + aff_0 = aff.place(0.0, 0.0, 0.0, 0.0); + inv_0 = aff_0.inv(); + + minmax(-0.5, -0.5, 0.5, 0.5, aff_0, + negradx_, negrady_, posradx_, posrady_); + minmax(negradx_ - rad_x, negrady_ - rad_y, posradx_ + rad_x, posrady_ + rad_y, inv_0, + negradu_, negradv_, posradu_, posradv_); + + ///////////////////////////////////////////////////////// + // INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + /* +double flatradx_ = posradx_; +double flatrady_ = posrady_; +int flatradu = CEIL (posradu_) - 1; +int flatradv = CEIL (posradv_) - 1; + +switch (RASRAS (rin->type, rout->type)) + { + case RASRAS (RAS_RGB, RAS_RGB): + rop_resample_rgb (rin, rout, aff, invrot, + rowf, pmin, pmax, colf, qmin, qmax, nrows, + flatradu, flatradv, flatradx_, flatrady_, + rown, nocdiamx, coln, nocdiamy); + + CASE RASRAS (RAS_GR8, RAS_GR8): + rop_resample_gr8 (rin, rout, aff, invrot, + rowf, pmin, pmax, colf, qmin, qmax, nrows, + flatradu, flatradv, flatradx_, flatrady_, + rown, nocdiamx, coln, nocdiamy); + + CASE RASRAS (RAS_WB, RAS_GR8): + rop_resample_wb (rin, rout, aff, invrot, + rowf, pmin, pmax, colf, qmin, qmax, nrows, + flatradu, flatradv, flatradx_, flatrady_, + rown, nocdiamx, coln, nocdiamy); + + DEFAULT: + msg (MSG_IE, "rop_resample: unsupported raster combination"); + return; + } + */ + + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + //free_nocalc (coln); + if (coln) + delete (coln); + //free_nocalc (rown); + if (rown) + delete (rown); + free_filter(colf, rout->getLy()); + free_filter(rowf, rout->getLx()); +//----NON GESTIAMO ANCORA EXTRA BUFFER +//rop_resample_extra (rin, rout, aff); +#endif +} + +//----------------------------------------------------------------------------- + +typedef struct + { + TUINT32 val; + double tot; +} BLOB24; + +//----------------------------------------------------------------------------- + +#define MINOREQ(x, a) ((x) >= 0 && (x) <= (a)) +#define MINOR(x, a) ((x) >= 0 && (x) < (a)) + +//----------------------------------------------------------------------------- +} //namespace + +#ifndef TNZCORE_LIGHT + +namespace +{ +void do_resample(TRasterCM32P rout, const TRasterCM32P &rin, const TAffine &aff) +{ + TAffine inv; + int lx, ly, mx, my; + int lu, lv, mu, mv; + int x, y, u, v; + double u_0, v_0, u_, v_; + double fu, fv, gu, gv; + int i, j; + int wrapin, wrapout; + TUINT32 *bufin_tcm, *bufout_tcm; + TUINT32 *in_tcm, *out_tcm; + TUINT32 tcm[4]; + double w[4]; + TUINT32 transp; + BLOB24 color_blob[4], new_color_blob; + BLOB24 pencil_blob[4], new_pencil_blob; + int color_blobs; + int pencil_blobs; + bool some_pencil; + double tone_tot; + TUINT32 color_mask, pencil_mask; + TUINT32 tone_mask; + int tone; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) /* immagine out vuota */ + return; + + rout->lock(); + + if (!(rin->getLx() > 0 && rin->getLy() > 0)) /* immagine in vuota */ + { + for (y = 0; y < rout->getLy(); y++) + for (x = 0; x < rout->getLx(); x++) + ((TUINT32 *)rout->getRawData())[x + y * rout->getWrap()] = 0xff; + rout->unlock(); + return; + } + + rin->lock(); + + bufin_tcm = (TUINT32 *)rin->getRawData(); + bufout_tcm = (TUINT32 *)rout->getRawData(); + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + lu = rin->getLx(); + mu = lu - 1; + lv = rin->getLy(); + mv = lv - 1; + lx = rout->getLx(); + mx = lx - 1; + ly = rout->getLy(); + my = ly - 1; + inv = aff.inv(); + + pencil_mask = TPixelCM32::getInkMask(); + color_mask = TPixelCM32::getPaintMask(); + tone_mask = TPixelCM32::getToneMask(); + + transp = tone_mask; + + assert(tone_mask & 0x1); //Ensure that tone lies in the less significative bits + + //deal with every output line independently + for (y = 0; y < ly; y++) { + //Take inv*(0,y) + u_0 = inv.a12 * y + inv.a13; + v_0 = inv.a22 * y + inv.a23; + + out_tcm = bufout_tcm + wrapout * y; + x = 0; + + //Place transparent pixels until we reach a useful source pos. + for (; x < lx; x++) { + //Add inv*(x,0) and floor it + u_ = u_0 + x * inv.a11; + u = tfloor(u_); + v_ = v_0 + x * inv.a21; + v = tfloor(v_); + if (MINOREQ(u + 1, lu) && MINOREQ(v + 1, lv)) //u>=-1 && u=-1 && v=0 && u=0 && v 0 && color_blob[j].tot > color_blob[j - 1].tot; j--) + tswap(color_blob[j], color_blob[j - 1]); + new_pencil_blob.val = tcm[i] & pencil_mask; + new_pencil_blob.tot = (tone_mask - tone) * w[i]; + for (j = 0; j < pencil_blobs; j++) + if (pencil_blob[j].val == new_pencil_blob.val) + break; + if (j < pencil_blobs) + pencil_blob[j].tot += new_pencil_blob.tot; + else + pencil_blob[pencil_blobs++] = new_pencil_blob; + for (; j > 0 && pencil_blob[j].tot > pencil_blob[j - 1].tot; j--) + tswap(pencil_blob[j], pencil_blob[j - 1]); + } + tone = troundp(tone_tot); + //if (some_pencil && (TUINT32)tone == tone_mask) + // tone--; + //if (color_blob[0].val==0 && pencil_blob[0].val==0) + // tone = 255; + *out_tcm++ = color_blob[0].val | pencil_blob[0].val | tone; + } + } + + //Deal with useful source positions on the output line's pre-image + for (; x < lx; x++) { + u_ = u_0 + x * inv.a11; + u = tfloor(u_); + v_ = v_0 + x * inv.a21; + v = tfloor(v_); + if (!(MINOR(u, lu) && MINOR(v, lv))) //u<0 || u>=lu || v<0 || v>=lv + break; + in_tcm = bufin_tcm + u + v * wrapin; //Take the associated input pixel pointer + tcm[0] = in_tcm[0]; + if (u < lu - 1 && v < lv - 1) { + //Also take their 4 next neighours (we shall perform a kinf of bilinear interpolation) + tcm[1] = in_tcm[1]; + tcm[2] = in_tcm[wrapin]; + tcm[3] = in_tcm[wrapin + 1]; + } else { + //Eventually, simulate the off-boundary ones + tcm[1] = (u == lu - 1) ? in_tcm[0] : in_tcm[1]; + tcm[2] = (v == lv - 1) ? in_tcm[0] : in_tcm[wrapin]; + tcm[3] = (u == lu - 1 || v == lv - 1) ? in_tcm[0] : in_tcm[wrapin + 1]; + } + if (tcm[0] == tcm[1] && tcm[1] == tcm[2] && tcm[2] == tcm[3]) + *out_tcm++ = tcm[0]; //If they are all equal, it's a copy-op + else { + //Otherwise, take the bilinear coordinates + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + w[0] = gu * gv; + w[2] = gu * fv; //And the associated weights + w[1] = fu * gv; + w[3] = fu * fv; + color_blobs = pencil_blobs = 0; + tone_tot = 0.0; + some_pencil = false; + //Examine all neighbouring pixels + for (i = 0; i < 4; i++) { + tone = tcm[i] & tone_mask; //Take the tone + if ((TUINT32)tone != tone_mask) + some_pencil = true; + tone_tot += tone * w[i]; //Build the weighted tone sum + new_color_blob.val = tcm[i] & color_mask; + new_color_blob.tot = tone * w[i]; //And the weighted paint tone for this pixel + //Fill in the different colors found in an array. Equal colors are stored as one + //with summed weighted total tone. + for (j = 0; j < color_blobs; j++) + if (color_blob[j].val == new_color_blob.val) + break; + if (j < color_blobs) + color_blob[j].tot += new_color_blob.tot; + else + color_blob[color_blobs++] = new_color_blob; + //Sort the stored colors for decreasing weighted total tone + for (; j > 0 && color_blob[j].tot > color_blob[j - 1].tot; j--) + tswap(color_blob[j], color_blob[j - 1]); + + //Deal the same way with ink colors. + new_pencil_blob.val = tcm[i] & pencil_mask; + new_pencil_blob.tot = (tone_mask - tone) * w[i]; + for (j = 0; j < pencil_blobs; j++) + if (pencil_blob[j].val == new_pencil_blob.val) + break; + if (j < pencil_blobs) + pencil_blob[j].tot += new_pencil_blob.tot; + else + pencil_blob[pencil_blobs++] = new_pencil_blob; + for (; j > 0 && pencil_blob[j].tot > pencil_blob[j - 1].tot; j--) + tswap(pencil_blob[j], pencil_blob[j - 1]); + } + + tone = tround(tone_tot); + //if (some_pencil && (TUINT32)tone == tone_mask) + // tone--; + //if (color_blob[0].val==0 && pencil_blob[0].val==0) + // tone = 255; + + //The output colors shall be the ones with maximum weighted total tone, + //with the overall total tone as output tone. + *out_tcm++ = color_blob[0].val | pencil_blob[0].val | tone; + } + } + + //Again, deal with border pixels at the end of line's pre-image + for (; x < lx; x++) { + u_ = u_0 + x * inv.a11; + u = tfloor(u_); + v_ = v_0 + x * inv.a21; + v = tfloor(v_); + if (!(MINOREQ(u + 1, lu) && MINOREQ(v + 1, lv))) //u<-1 || u>=lu || v<-1 || v>=lv + break; + in_tcm = bufin_tcm + u + v * wrapin; + bool u0 = MINOREQ(u, mu); + bool v0 = MINOREQ(v, mv); + bool u1 = MINOREQ(u + 1, mv); + bool v1 = MINOREQ(v + 1, mv); + + tcm[0] = (u0 && v0) ? in_tcm[0] : transp; + tcm[1] = (u1 && v0) ? in_tcm[1] : transp; + tcm[2] = (u0 && v1) ? in_tcm[wrapin] : transp; + tcm[3] = (u1 && v1) ? in_tcm[wrapin + 1] : transp; + if (tcm[0] == tcm[1] && tcm[1] == tcm[2] && tcm[2] == tcm[3]) + *out_tcm++ = tcm[0]; + else { + fu = u_ - u; + gu = 1. - fu; + fv = v_ - v; + gv = 1. - fv; + w[0] = gu * gv; + w[2] = gu * fv; + w[1] = fu * gv; + w[3] = fu * fv; + color_blobs = pencil_blobs = 0; + tone_tot = 0.0; + some_pencil = false; + for (i = 0; i < 4; i++) { + tone = tcm[i] & tone_mask; + if ((TUINT32)tone != tone_mask) + some_pencil = true; + tone_tot += tone * w[i]; + new_color_blob.val = tcm[i] & color_mask; + new_color_blob.tot = tone * w[i]; + for (j = 0; j < color_blobs; j++) + if (color_blob[j].val == new_color_blob.val) + break; + if (j < color_blobs) + color_blob[j].tot += new_color_blob.tot; + else + color_blob[color_blobs++] = new_color_blob; + for (; j > 0 && color_blob[j].tot > color_blob[j - 1].tot; j--) + tswap(color_blob[j], color_blob[j - 1]); + new_pencil_blob.val = tcm[i] & pencil_mask; + new_pencil_blob.tot = (tone_mask - tone) * w[i]; + for (j = 0; j < pencil_blobs; j++) + if (pencil_blob[j].val == new_pencil_blob.val) + break; + if (j < pencil_blobs) + pencil_blob[j].tot += new_pencil_blob.tot; + else + pencil_blob[pencil_blobs++] = new_pencil_blob; + for (; j > 0 && pencil_blob[j].tot > pencil_blob[j - 1].tot; j--) + tswap(pencil_blob[j], pencil_blob[j - 1]); + } + tone = troundp(tone_tot); + //if (some_pencil && (TUINT32)tone == tone_mask) + // tone--; + //if (color_blob[0].val==0 && pencil_blob[0].val==0) + // tone = 255; + + *out_tcm++ = color_blob[0].val | pencil_blob[0].val | tone; + } + } + + //Finally, deal with out-of-source pixels at the end of line's pre-image + for (; x < lx; x++) + *out_tcm++ = transp; + } + + rin->unlock(); + rout->unlock(); +} + +//----------------------------------------------------------------------------- + +#ifdef WIN32 +template +void resample_main_cm32_rgbm_SSE2(TRasterPT rout, const TRasterCM32P &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter, TPalette *palette) +{ + __m128i zeros = _mm_setzero_si128(); + const TPixelCM32 *buffer_in; + T *buffer_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int inside_offset_u, inside_offset_v; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + double outside_min_u_, outside_min_v_; + double outside_max_u_, outside_max_v_; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + T pix_value; + T default_value(0, 0, 0, 0); + float weight; + float sum_weights; + float inv_sum_weights; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + T *pix_out; + + __m128 sum_contribs_packed; + __m128 pix_value_packed; + __m128 weight_packed; + + __m128 zeros2 = _mm_setzero_ps(); + + float maxChannelValue = (float)T::maxChannelValue; + __m128 maxChanneValue_packed = _mm_load1_ps(&maxChannelValue); + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + resample_clear_rgbm(rout, default_value); + return; + } + calc = 0; + calc_allocsize = 0; + + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + inside_offset_u = -min_pix_ref_u; + inside_offset_v = -min_pix_ref_v; + inside_limit_u = lu - max_pix_ref_u - inside_offset_u; + inside_limit_v = lv - max_pix_ref_v - inside_offset_v; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u_ = -0.5; + outside_min_v_ = -0.5; + outside_max_u_ = lu - 0.5; + outside_max_v_ = lv - 0.5; + + int count = palette->getStyleCount(); + int count2 = tmax(count, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + TPixelFloat *paints = (TPixelFloat *)_aligned_malloc(count2 * sizeof(TPixelFloat), 16); + TPixelFloat *inks = (TPixelFloat *)_aligned_malloc(count2 * sizeof(TPixelFloat), 16); + + std::vector paints2(count2); + std::vector inks2(count2); + for (i = 0; i < palette->getStyleCount(); i++) { + TPixel32 color = ::premultiply(palette->getStyle(i)->getAverageColor()); + paints[i] = inks[i] = TPixelFloat(color); + paints2[i] = inks2[i] = color; + } + + float maxTone = (float)TPixelCM32::getMaxTone(); + __m128 den_packed = _mm_load1_ps(&maxTone); + + for (out_y = 0, out_y_ = 0.0; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.0; out_x < lx; out_x++, out_x_ += 1.0) { + pix_out = buffer_out + out_y * wrap_out + out_x; + + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + if (inside_nonempty && + (UINT)(ref_u - inside_offset_u) < inside_limit_u && + (UINT)(ref_v - inside_offset_v) < inside_limit_v) { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + + sum_contribs_packed = _mm_setzero_ps(); + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + int pix_in_pos = pix_u + pix_v * wrap_in; + const TPixelCM32 *pix_in = buffer_in + pix_in_pos; + int tone = pix_in->getTone(); + int paint = pix_in->getPaint(); + int ink = pix_in->getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value_packed = _mm_load_ps((float *)&(paints[paint])); + else if (tone == 0) + pix_value_packed = _mm_load_ps((float *)&(inks[ink])); + else { + float tt = (float)tone; + blendBySSE2( + pix_value_packed, // il valore calcolato + (float *)&(inks[ink]), + (float *)&(paints[paint]), + &tt, den_packed, zeros); + } + + weight_packed = _mm_load1_ps(&weight); + sum_contribs_packed = _mm_add_ps(sum_contribs_packed, _mm_mul_ps(pix_value_packed, weight_packed)); + + sum_weights += weight; + } + + inv_sum_weights = 1.0f / sum_weights; + __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); + + __m128 out_fval_packed = _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); + out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); + out_fval_packed = _mm_min_ps(out_fval_packed, maxChanneValue_packed); + + __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); + out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); + out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); + } else { + int pix_in_pos = ref_u + ref_v * wrap_in; + const TPixelCM32 *pix_in = buffer_in + pix_in_pos; + int tone = pix_in->getTone(); + int paint = pix_in->getPaint(); + int ink = pix_in->getInk(); + + if (tone == TPixelCM32::getMaxTone()) + *pix_out = paints2[paint]; + else if (tone == 0) + *pix_out = inks2[ink]; + else + *pix_out = blend(inks2[ink], paints2[paint], tone, TPixelCM32::getMaxTone()); + } + } else if (outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_packed = _mm_setzero_ps(); + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + int pix_in_pos = pix_u + pix_v * wrap_in; + const TPixelCM32 *pix_in = buffer_in + pix_in_pos; + int tone = pix_in->getTone(); + int paint = pix_in->getPaint(); + int ink = pix_in->getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value_packed = _mm_load_ps((float *)&(paints[paint])); + else if (tone == 0) + pix_value_packed = _mm_load_ps((float *)&(inks[ink])); + else { + float tt = (float)tone; + blendBySSE2( + pix_value_packed, // il valore calcolato + (float *)&(inks[ink]), (float *)&(paints[paint]), + &tt, den_packed, zeros); + } + + weight_packed = _mm_load1_ps(&weight); + sum_contribs_packed = _mm_add_ps(sum_contribs_packed, _mm_mul_ps(pix_value_packed, weight_packed)); + + sum_weights += weight; + } + + inv_sum_weights = 1.0f / sum_weights; + + __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); + __m128 out_fval_packed = _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); + out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); + out_fval_packed = _mm_min_ps(out_fval_packed, maxChanneValue_packed); + + __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); + out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); + out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); + } else { + int pix_in_pos = ref_u + ref_v * wrap_in; + const TPixelCM32 *pix_in = buffer_in + pix_in_pos; + int tone = pix_in->getTone(); + int paint = pix_in->getPaint(); + int ink = pix_in->getInk(); + + if (tone == TPixelCM32::getMaxTone()) + *pix_out = paints2[paint]; + else if (tone == 0) + *pix_out = inks2[ink]; + else + *pix_out = blend(inks2[ink], paints2[paint], tone, TPixelCM32::getMaxTone()); + } + } else { + *pix_out = default_value; + } + } + } + if (calc) + delete[] calc; +} + +#endif + +/*---------------------------------------------------------------------------*/ + +namespace +{ + +template +void resample_main_cm32_rgbm_bigradius(TRasterPT rout, const TRasterCM32P &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter, + TPalette *palette) +{ + // bigradius: cambia solo che i sum_contribs sono double invece che int + + const TPixelCM32 *buffer_in; + T *buffer_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int inside_offset_u, inside_offset_v; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + double outside_min_u_, outside_min_v_; + double outside_max_u_, outside_max_v_; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + T pix_value; + T default_value; + float weight; + float sum_weights; + double inv_sum_weights; + double sum_contribs_r, sum_contribs_g, sum_contribs_b, sum_contribs_m; + double out_fval_r, out_fval_g, out_fval_b, out_fval_m; + int out_value_r, out_value_g, out_value_b, out_value_m; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + T *pix_out; + + default_value.r = 0; + default_value.g = 0; + default_value.b = 0; + default_value.m = 0; + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + rout->clear(); + return; + } + + calc = 0; + calc_allocsize = 0; + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + inside_offset_u = -min_pix_ref_u; + inside_offset_v = -min_pix_ref_v; + inside_limit_u = lu - max_pix_ref_u - inside_offset_u; + inside_limit_v = lv - max_pix_ref_v - inside_offset_v; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u_ = -0.5; + outside_min_v_ = -0.5; + outside_max_u_ = lu - 0.5; + outside_max_v_ = lv - 0.5; + + int colorCount = palette->getStyleCount(); + colorCount = tmax(colorCount, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + std::vector paints(colorCount); + std::vector inks(colorCount); + for (i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + for (out_y = 0, out_y_ = 0.0; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.0; out_x < lx; out_x++, out_x_ += 1.0) { + pix_out = buffer_out + out_y * wrap_out + out_x; + + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + if (inside_nonempty && + (UINT)(ref_u - inside_offset_u) < inside_limit_u && + (UINT)(ref_v - inside_offset_v) < inside_limit_v) { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value = Converter::convert(paints[paint]); + else if (tone == 0) + pix_value = Converter::convert(inks[ink]); + else + pix_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + + } else { + // *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + + int pix_in_pos = ref_u + ref_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + *pix_out = Converter::convert(paints[paint]); + else if (tone == 0) + *pix_out = Converter::convert(inks[ink]); + else + *pix_out = Converter::convert(blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + } + } else if (outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + // pix_value = buffer_in[pix_u + pix_v * wrap_in]; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value = Converter::convert(paints[paint]); + else if (tone == 0) + pix_value = Converter::convert(inks[ink]); + else + pix_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + } else { + int pix_in_pos = ref_u + ref_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + *pix_out = Converter::convert(paints[paint]); + else if (tone == 0) + *pix_out = Converter::convert(inks[ink]); + else + *pix_out = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + } + } else { + *pix_out = default_value; + } + } + } + + if (calc) + delete[] calc; +} +} + +/*---------------------------------------------------------------------------*/ + +template +void resample_main_cm32_rgbm(TRasterPT rout, const TRasterCM32P &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter, + TPalette *palette) +{ + const TPixelCM32 *buffer_in; + T *buffer_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int inside_offset_u, inside_offset_v; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + double outside_min_u_, outside_min_v_; + double outside_max_u_, outside_max_v_; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + T pix_value; + T default_value(0, 0, 0, 0); + int weight; + int sum_weights; + double inv_sum_weights; + int sum_contribs_r, sum_contribs_g, sum_contribs_b, sum_contribs_m; + double out_fval_r, out_fval_g, out_fval_b, out_fval_m; + int out_value_r, out_value_g, out_value_b, out_value_m; + T out_value; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (n_pix >= 512 || T::maxChannelValue > 255) { + resample_main_cm32_rgbm_bigradius(rout, rin, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter, palette); + return; + } + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + resample_clear_rgbm(rout, default_value); + return; + } + calc = 0; + calc_allocsize = 0; + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + inside_offset_u = -min_pix_ref_u; + inside_offset_v = -min_pix_ref_v; + inside_limit_u = lu - max_pix_ref_u - inside_offset_u; + inside_limit_v = lv - max_pix_ref_v - inside_offset_v; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u_ = -0.5; + outside_min_v_ = -0.5; + outside_max_u_ = lu - 0.5; + outside_max_v_ = lv - 0.5; + + int colorCount = palette->getStyleCount(); + colorCount = tmax(colorCount, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + std::vector paints(colorCount); + std::vector inks(colorCount); + for (i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + for (out_y = 0, out_y_ = 0.0; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.0; out_x < lx; out_x++, out_x_ += 1.0) { + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + if (inside_nonempty && + (UINT)(ref_u - inside_offset_u) < inside_limit_u && + (UINT)(ref_v - inside_offset_v) < inside_limit_v) { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + // pix_value = buffer_in[pix_u + pix_v * wrap_in]; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value = Converter::convert(paints[paint]); + else if (tone == 0) + pix_value = Converter::convert(inks[ink]); + else + pix_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + out_value.r = out_value_r; + out_value.g = out_value_g; + out_value.b = out_value_b; + out_value.m = out_value_m; + } else { + // out_value = buffer_in[ref_u + ref_v * wrap_in]; + int pix_in_pos = ref_u + ref_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + out_value = Converter::convert(paints[paint]); + else if (tone == 0) + out_value = Converter::convert(inks[ink]); + else + out_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + } + } else if (outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + // pix_value = buffer_in[pix_u + pix_v * wrap_in]; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + pix_value = Converter::convert(paints[paint]); + else if (tone == 0) + pix_value = Converter::convert(inks[ink]); + else + pix_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(T::maxChannelValue, out_value_r); + notMoreThan(T::maxChannelValue, out_value_g); + notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(T::maxChannelValue, out_value_m); + out_value.r = out_value_r; + out_value.g = out_value_g; + out_value.b = out_value_b; + out_value.m = out_value_m; + } else { + // out_value = buffer_in[ref_u + ref_v * wrap_in]; + + int pix_in_pos = ref_u + ref_v * wrap_in; + int tone = buffer_in[pix_in_pos].getTone(); + int paint = buffer_in[pix_in_pos].getPaint(); + int ink = buffer_in[pix_in_pos].getInk(); + + if (tone == TPixelCM32::getMaxTone()) + out_value = Converter::convert(paints[paint]); + else if (tone == 0) + out_value = Converter::convert(inks[ink]); + else + out_value = Converter::convert( + blend(inks[ink], paints[paint], tone, TPixelCM32::getMaxTone())); + } + } else { + out_value = default_value; + } + + buffer_out[out_x + out_y * wrap_out] = out_value; + } + } + + if (calc) + delete[] calc; +} + +//--------------------------------------------------------------------------- + +void resample_cm32_rgbm(TRaster32P rout, const TRasterCM32P &rin, + const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, + int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, + int n_pix, + int *pix_ref_u, int *pix_ref_v, + int *pix_ref_f, int *pix_ref_g, + short *filter, TPalette *palette) +{ + const TPixelCM32 *buffer_in; + /*T*/ TPixel32 *buffer_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int inside_offset_u, inside_offset_v; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + double outside_min_u_, outside_min_v_; + double outside_max_u_, outside_max_v_; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + /*T*/ TPixel32 pix_value; + /*T*/ TPixel32 default_value(0, 0, 0, 0); + int weight; + int sum_weights; + double inv_sum_weights; + int sum_contribs_r, sum_contribs_g, sum_contribs_b, sum_contribs_m; + double out_fval_r, out_fval_g, out_fval_b, out_fval_m; + int out_value_r, out_value_g, out_value_b, out_value_m; + /*T*/ TPixel32 out_value; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (n_pix >= 512 || /*T*/ TPixel32::maxChannelValue > 255) { + assert(false); + /* + resample_main_rgbm_bigradius( rout, rin, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter ); + */ + return; + } + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) + return; + + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + resample_clear_rgbm(rout, default_value); + return; + } + + int colorCount = palette->getStyleCount(); + colorCount = tmax(colorCount, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + std::vector paints(colorCount); + std::vector inks(colorCount); + for (i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + calc = 0; + calc_allocsize = 0; + create_calc(rin, min_pix_ref_u, max_pix_ref_u, + min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + inside_offset_u = -min_pix_ref_u; + inside_offset_v = -min_pix_ref_v; + inside_limit_u = lu - max_pix_ref_u - inside_offset_u; + inside_limit_v = lv - max_pix_ref_v - inside_offset_v; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u_ = -0.5; + outside_min_v_ = -0.5; + outside_max_u_ = lu - 0.5; + outside_max_v_ = lv - 0.5; + + for (out_y = 0, out_y_ = 0.0; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.0; out_x < lx; out_x++, out_x_ += 1.0) { + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + if (inside_nonempty && + (UINT)(ref_u - inside_offset_u) < inside_limit_u && + (UINT)(ref_v - inside_offset_v) < inside_limit_v) { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & 1)) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + //pix_value = buffer_in[pix_u + pix_v * wrap_in]; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int t = buffer_in[pix_in_pos].getTone(); + int p = buffer_in[pix_in_pos].getPaint(); + int i = buffer_in[pix_in_pos].getInk(); + + if (t == TPixelCM32::getMaxTone()) + pix_value = paints[p]; + else if (t == 0) + pix_value = inks[i]; + else + pix_value = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_r); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_g); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_b); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_m); + out_value.r = out_value_r; + out_value.g = out_value_g; + out_value.b = out_value_b; + out_value.m = out_value_m; + } else { + int pix_in_pos = ref_u + ref_v * wrap_in; + int t = buffer_in[pix_in_pos].getTone(); + int p = buffer_in[pix_in_pos].getPaint(); + int i = buffer_in[pix_in_pos].getInk(); + + if (t == TPixelCM32::getMaxTone()) + out_value = paints[p]; + else if (t == 0) + out_value = inks[i]; + else + out_value = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + + // out_value = buffer_in[ref_u + ref_v * wrap_in]; + } + } else if (outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (filter[pix_out_f] * filter[pix_out_g]) >> 16; + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + //pix_value = buffer_in[pix_u + pix_v * wrap_in]; + + int pix_in_pos = pix_u + pix_v * wrap_in; + int t = buffer_in[pix_in_pos].getTone(); + int p = buffer_in[pix_in_pos].getPaint(); + int i = buffer_in[pix_in_pos].getInk(); + + if (t == TPixelCM32::getMaxTone()) + pix_value = paints[p]; + else if (t == 0) + pix_value = inks[i]; + else + pix_value = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + + sum_contribs_r += (int)pix_value.r * weight; + sum_contribs_g += (int)pix_value.g * weight; + sum_contribs_b += (int)pix_value.b * weight; + sum_contribs_m += (int)pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + notLessThan(0.0, out_fval_r); + notLessThan(0.0, out_fval_g); + notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = troundp(out_fval_r); + out_value_g = troundp(out_fval_g); + out_value_b = troundp(out_fval_b); + out_value_m = troundp(out_fval_m); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_r); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_g); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_b); + notMoreThan(/*T*/ TPixel32::maxChannelValue, out_value_m); + out_value.r = out_value_r; + out_value.g = out_value_g; + out_value.b = out_value_b; + out_value.m = out_value_m; + } else { + // out_value = buffer_in[ref_u + ref_v * wrap_in]; + + int pix_in_pos = ref_u + ref_v * wrap_in; + int t = buffer_in[pix_in_pos].getTone(); + int p = buffer_in[pix_in_pos].getPaint(); + int i = buffer_in[pix_in_pos].getInk(); + + if (t == TPixelCM32::getMaxTone()) + out_value = paints[p]; + else if (t == 0) + out_value = inks[i]; + else + out_value = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + } + } else { + out_value = default_value; + } + + buffer_out[out_x + out_y * wrap_out] = out_value; + } + } + + if (calc) + delete[] calc; +} + +//--------------------------------------------------------------------------- + +template +void rop_resample_rgbm_2(TRasterPT rout, const TRasterCM32P &rin, + const TAffine &aff, TRop::ResampleFilterType flt_type, double blur, TPalette *palette) +{ +#define FILTER_RESOLUTION 1024 +#define MAX_FILTER_VAL 32767 + +#ifdef USE_STATIC_VARS + static TRop::ResampleFilterType current_flt_type = TRop::None; + static short *filter_array = 0; + static short *filter = 0; + static int min_filter_fg, max_filter_fg; + static int filter_array_size = 0; + static int n_pix = 0; + static int *pix_ref_u = 0; + static int *pix_ref_v = 0; + static int *pix_ref_f = 0; + static int *pix_ref_g = 0; + static int current_max_n_pix = 0; +#else + short *filter_array = 0; + short *filter = 0; + int min_filter_fg, max_filter_fg; + int filter_array_size = 0; + int n_pix = 0; + int *pix_ref_u = 0; + int *pix_ref_v = 0; + int *pix_ref_f = 0; + int *pix_ref_g = 0; + int current_max_n_pix = 0; +#endif + + int filter_st_radius; + int filter_fg_radius; + int filter_size; + int f; + double s_; + double weight_; + int weight; + TAffine aff_uv2xy; + TAffine aff_xy2uv; + TAffine aff0_uv2xy; + TAffine aff0_xy2st; + TAffine aff0_uv2st; + TAffine aff0_st2fg; + TAffine aff0_uv2fg; + TAffine aff0_fg2uv; + double scale_x, scale_y; + double inv_blur; + int max_n_pix; + double min_pix_out_u_, min_pix_out_v_; + double max_pix_out_u_, max_pix_out_v_; + int min_pix_ref_u, min_pix_ref_v; + int max_pix_ref_u, max_pix_ref_v; + int cur_pix_ref_u, cur_pix_ref_v; + double cur_pix_ref_f_, cur_pix_ref_g_; + int cur_pix_ref_f, cur_pix_ref_g; + double min_ref_out_f_, min_ref_out_g_; + double max_ref_out_f_, max_ref_out_g_; + int min_ref_out_f, min_ref_out_g; + int max_ref_out_f, max_ref_out_g; + int min_pix_ref_f, min_pix_ref_g; + int max_pix_ref_f, max_pix_ref_g; + int min_pix_out_f, min_pix_out_g; + int max_pix_out_f, max_pix_out_g; + int min_pix_out_fg; + int max_pix_out_fg; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + assert(flt_type != TRop::None); + + filter_st_radius = get_filter_radius(flt_type); + filter_fg_radius = filter_st_radius * FILTER_RESOLUTION; + + aff_uv2xy = aff; + aff0_uv2xy = aff_uv2xy.place(0.0, 0.0, 0.0, 0.0); + aff_xy2uv = aff_uv2xy.inv(); + + scale_x = sqrt(sq(aff_uv2xy.a11) + sq(aff_uv2xy.a12)); + scale_y = sqrt(sq(aff_uv2xy.a21) + sq(aff_uv2xy.a22)); + aff0_xy2st = TScale((scale_x > 1.0) ? 1.0 / scale_x : 1.0, + (scale_y > 1.0) ? 1.0 / scale_y : 1.0); + + if (blur > 1.0) //per ora il blur e' 1.0 + { + inv_blur = 1.0 / blur; + aff0_xy2st = TScale(inv_blur, inv_blur) * aff0_xy2st; + } + + aff0_uv2st = aff0_xy2st * aff0_uv2xy; + aff0_st2fg = TScale(FILTER_RESOLUTION, FILTER_RESOLUTION); + aff0_uv2fg = aff0_st2fg * aff0_uv2st; + aff0_fg2uv = aff0_uv2fg.inv(); + + minmax(-filter_fg_radius, -filter_fg_radius, + filter_fg_radius, filter_fg_radius, + aff0_fg2uv, + min_pix_out_u_, min_pix_out_v_, + max_pix_out_u_, max_pix_out_v_); + + min_pix_ref_u = intGT(min_pix_out_u_); + min_pix_ref_v = intGT(min_pix_out_v_); + max_pix_ref_u = intLT(max_pix_out_u_) + 1; + max_pix_ref_v = intLT(max_pix_out_v_) + 1; + + if (blur <= 1.0) { + if (aff_uv2xy.a12 == 0.0 && aff_uv2xy.a21 == 0.0) { + if (aff_uv2xy.a11 == 1.0 && isInt(aff_uv2xy.a13)) { + min_pix_ref_u = 0; + max_pix_ref_u = 0; + } + if (aff_uv2xy.a22 == 1.0 && isInt(aff_uv2xy.a23)) { + min_pix_ref_v = 0; + max_pix_ref_v = 0; + } + } else if (aff_uv2xy.a11 == 0.0 && aff_uv2xy.a22 == 0.0) { + if (aff_uv2xy.a12 == 1.0 && isInt(aff_uv2xy.a13)) { + min_pix_ref_v = 0; + max_pix_ref_v = 0; + } + if (aff_uv2xy.a21 == 1.0 && isInt(aff_uv2xy.a23)) { + min_pix_ref_u = 0; + max_pix_ref_u = 0; + } + } + } + + max_n_pix = (max_pix_ref_u - min_pix_ref_u + 1) * + (max_pix_ref_v - min_pix_ref_v + 1); + + if (max_n_pix > current_max_n_pix) { + current_max_n_pix = max_n_pix; + if (pix_ref_u) + delete[] pix_ref_u; + pix_ref_u = new int[current_max_n_pix]; + if (pix_ref_v) + delete[] pix_ref_v; + pix_ref_v = new int[current_max_n_pix]; + if (pix_ref_f) + delete[] pix_ref_f; + pix_ref_f = new int[current_max_n_pix]; + if (pix_ref_g) + delete[] pix_ref_g; + pix_ref_g = new int[current_max_n_pix]; + assert(pix_ref_u && pix_ref_v && pix_ref_f && pix_ref_g); + } + + minmax(-1, -1, 0, 0, + aff0_uv2fg, + min_ref_out_f_, min_ref_out_g_, + max_ref_out_f_, max_ref_out_g_); + + min_ref_out_f = tround(min_ref_out_f_); + min_ref_out_g = tround(min_ref_out_g_); + max_ref_out_f = tround(max_ref_out_f_); + max_ref_out_g = tround(max_ref_out_g_); + min_pix_ref_f = -filter_fg_radius - max_ref_out_f; + min_pix_ref_g = -filter_fg_radius - max_ref_out_g; + max_pix_ref_f = filter_fg_radius - min_ref_out_f; + max_pix_ref_g = filter_fg_radius - min_ref_out_g; + + min_pix_out_f = c_maxint; + min_pix_out_g = c_maxint; + max_pix_out_f = c_minint; + max_pix_out_g = c_minint; + n_pix = 0; + for (cur_pix_ref_v = min_pix_ref_v; + cur_pix_ref_v <= max_pix_ref_v; + cur_pix_ref_v++) + for (cur_pix_ref_u = min_pix_ref_u; + cur_pix_ref_u <= max_pix_ref_u; + cur_pix_ref_u++) { + cur_pix_ref_f_ = affMV1(aff0_uv2fg, cur_pix_ref_u, cur_pix_ref_v); + cur_pix_ref_g_ = affMV2(aff0_uv2fg, cur_pix_ref_u, cur_pix_ref_v); + cur_pix_ref_f = tround(cur_pix_ref_f_); + cur_pix_ref_g = tround(cur_pix_ref_g_); + if (min_pix_ref_f <= cur_pix_ref_f && cur_pix_ref_f <= max_pix_ref_f && + min_pix_ref_g <= cur_pix_ref_g && cur_pix_ref_g <= max_pix_ref_g) { + pix_ref_u[n_pix] = cur_pix_ref_u; + pix_ref_v[n_pix] = cur_pix_ref_v; + pix_ref_f[n_pix] = cur_pix_ref_f; + pix_ref_g[n_pix] = cur_pix_ref_g; + notMoreThan(cur_pix_ref_f + min_ref_out_f, min_pix_out_f); + notMoreThan(cur_pix_ref_g + min_ref_out_g, min_pix_out_g); + notLessThan(cur_pix_ref_f + max_ref_out_f, max_pix_out_f); + notLessThan(cur_pix_ref_g + max_ref_out_g, max_pix_out_g); + n_pix++; + } + } + assert(n_pix > 0); + +#ifdef USE_STATIC_VARS + if (flt_type != current_flt_type) { + current_flt_type = flt_type; +#endif + min_filter_fg = -filter_fg_radius - FILTER_RESOLUTION * 3 / 2; + max_filter_fg = filter_fg_radius + FILTER_RESOLUTION * 3 / 2; + filter_size = max_filter_fg - min_filter_fg + 1; + if (filter_size > filter_array_size) { + if (filter_array) + delete[] filter_array; + filter_array = new short[filter_size]; + assert(filter_array); + filter_array_size = filter_size; + } + filter = filter_array - min_filter_fg; + filter[0] = MAX_FILTER_VAL; + for (f = 1, s_ = 1.0 / FILTER_RESOLUTION; + f < filter_fg_radius; + f++, s_ += 1.0 / FILTER_RESOLUTION) { + weight_ = get_filter_value(flt_type, s_) * (double)MAX_FILTER_VAL; + weight = tround(weight_); + filter[f] = weight; + filter[-f] = weight; + } + for (f = filter_fg_radius; f <= max_filter_fg; f++) + filter[f] = 0; + for (f = -filter_fg_radius; f >= min_filter_fg; f--) + filter[f] = 0; + +#ifdef USE_STATIC_VARS + } +#endif + + min_pix_out_fg = tmin(min_pix_out_f, min_pix_out_g); + max_pix_out_fg = tmax(max_pix_out_f, max_pix_out_g); + if (min_pix_out_fg < min_filter_fg || max_pix_out_fg > max_filter_fg) { + filter_size = max_pix_out_fg - min_pix_out_fg + 1; + if (filter_size > filter_array_size) { + //controllare!! + //TREALLOC (filter_array, filter_size) + if (filter_array) + delete[] filter_array; + filter_array = new short[filter_size]; + + assert(filter_array); + filter_array_size = filter_size; + } + filter = filter_array - min_filter_fg; + if (min_pix_out_fg < min_filter_fg) { + int delta = min_filter_fg - min_pix_out_fg; + + for (f = max_filter_fg; f >= min_filter_fg; f--) + filter[f + delta] = filter[f]; + filter += delta; + for (f = min_filter_fg - 1; f >= min_pix_out_fg; f--) + filter[f] = 0; + min_filter_fg = min_pix_out_fg; + } + if (max_pix_out_fg > max_filter_fg) { + for (f = max_filter_fg + 1; f <= max_pix_out_fg; f++) + filter[f] = 0; + max_filter_fg = max_pix_out_fg; + } + } + +#ifdef WIN32 + TRaster32P rout32 = rout; + if ((TSystem::getCPUExtensions() & TSystem::CpuSupportsSse2) && rout32) + resample_main_cm32_rgbm_SSE2(rout32, rin, aff_xy2uv, aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter, palette); + else +#endif + resample_main_cm32_rgbm(rout, rin, aff_xy2uv, aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter, palette); + +#ifndef USE_STATIC_VARS + if (filter_array) + delete[] filter_array; + if (pix_ref_u) + delete[] pix_ref_u; + if (pix_ref_v) + delete[] pix_ref_v; + if (pix_ref_f) + delete[] pix_ref_f; + if (pix_ref_g) + delete[] pix_ref_g; +#endif + + ///////////////////////////////////////////////////////// + // INIZIO GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// + + /* +switch (RASRAS (rin->type, rout->type)) + { + case RASRAS (RAS_RGB_, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGB_): + __OR RASRAS (RAS_RGBM, RAS_RGBM): + resample_main_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_RGBM, RAS_RGBM64): + resample_main_rgbm_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_RGBM64, RAS_RGBM64): + resample_main_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM16, RAS_RGBM): + resample_main_cm16_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM24, RAS_RGBM): + resample_main_cm24_rgbm (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM16, RAS_RGBM64): + resample_main_cm16_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + CASE RASRAS (RAS_CM24, RAS_RGBM64): + resample_main_cm24_rgbm64 (rin, rout, + aff_xy2uv, + aff0_uv2fg, + min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, + n_pix, + pix_ref_u, pix_ref_v, + pix_ref_f, pix_ref_g, + filter); + + DEFAULT: + assert ( !"bad raster type combination"); + } +*/ + ///////////////////////////////////////////////////////// + // FINE GESTIONE ALTRI TIPI RASTER DA IMPLEMENTARE + ///////////////////////////////////////////////////////// +} + +//----------------------------------------------------------------------------- + +} //namespace + +//----------------------------------------------------------------------------- +void TRop::resample(const TRasterP &out, + const TRasterCM32P &in, + const TPaletteP palette, + const TAffine &aff, + TRop::ResampleFilterType filterType, + double blur) +{ + TRasterP rin = in; + TRaster32P rout32 = out; + in->lock(); + out->lock(); + if (rout32) + rop_resample_rgbm_2(rout32, rin, aff, filterType, blur, palette.getPointer()); + else { + TRaster64P rout64 = out; + if (rout64) + rop_resample_rgbm_2(rout64, rin, aff, filterType, blur, palette.getPointer()); + else { + in->unlock(); + out->unlock(); + throw TRopException("unsupported pixel type"); + return; + } + } + in->unlock(); + out->unlock(); +} + +#endif //TNZCORE_LIGHT + +void TRop::resample(const TRasterP &rout, const TRasterP &rin, + const TAffine &aff, ResampleFilterType filterType, double blur) +{ + rin->lock(); + rout->lock(); + + if (filterType == ClosestPixel || + filterType == Bilinear) { + if ((TRaster64P)rout || (TRaster64P)rin) + filterType = Triangle; + else { + quickResample(rout, rin, aff, filterType); + rin->unlock(); + rout->unlock(); + return; + } + } + + TRaster32P rout32 = rout, rin32 = rin; + if (rout32) { + if (!rin32) { + rin32 = TRaster32P(rin->getLx(), rin->getLy()); + TRop::convert(rin32, rin); + } + do_resample(rout32, rin32, aff, filterType, blur); + } else { +#ifndef TNZCORE_LIGHT + TRasterCM32P routCM32 = rout, rinCM32 = rin; + if (routCM32 && rinCM32) + do_resample(routCM32, rinCM32, aff); + else +#endif + { + TRaster64P rout64 = rout, rin64 = rin; + if (rout64) { + if (!rin64) { + rin64 = TRaster64P(rin->getLx(), rin->getLy()); + TRop::convert(rin64, rin); + } + do_resample(rout64, rin64, aff, filterType, blur); + } else { + TRasterGR8P routGR8 = rout, rinGR8 = rin; + TRaster32P rin32 = rin; + if (routGR8 && rinGR8) + do_resample(routGR8, rinGR8, aff, filterType, blur); + else if (routGR8 && rin32) + do_resample(routGR8, rin32, aff, filterType, blur); + else { + rin->unlock(); + rout->unlock(); + throw TRopException("unsupported pixel type"); + } + } + } + } + rin->unlock(); + rout->unlock(); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/trop/trgbmscale.cpp b/toonz/sources/common/trop/trgbmscale.cpp new file mode 100644 index 0000000..ff1f915 --- /dev/null +++ b/toonz/sources/common/trop/trgbmscale.cpp @@ -0,0 +1,306 @@ + + +#include "trop.h" +#include "tpixelutils.h" + +/* NOTE: Scale operations can be performed using Look-Up-Tables. + This is convenient for 8-bit channels, but perhaps not for 16-bit ones: + + In the 32-bit case, we perform max 256 channel operations, + equal to performing plain scale on a 16 x 16 image. + + In the 64-bit case, the cost is 65536 channel operations, + equal to performing plain scale on a 256 x 256 image. + + The raster size is used to discriminate LUT usage in the latter case +*/ + +//----------------------------------------------------------------------------- + +namespace +{ + +template +void buildLUT(Chan *lut, double a, double k, int chanLow, int chanHigh) +{ + int i, max = (std::numeric_limits::max)(); + + a += 0.5; //round rather than trunc + for (i = 0; i <= max; ++i) + lut[i] = tcrop((int)(a + i * k), chanLow, chanHigh); +} + +//----------------------------------------------------------------------------- + +template +void do_greyScale_lut(TRasterPT rout, TRasterPT rin, + double a, double k, int out0, int out1) +{ + assert(rout->getSize() == rin->getSize()); + + typedef typename T::Channel Channel; + int chanValuesCount = T::maxChannelValue + 1; + + int fac = chanValuesCount / 256; + out0 = tmax(fac * out0, 0), out1 = tmin(fac * out1, T::maxChannelValue); + + //Build lut + Channel *lut = new Channel[chanValuesCount]; + buildLUT(lut, a, k, out0, out1); + + //Perform scale + T *in, *end, *out; + + int y, lx = rin->getLx(), ly = rin->getLy(); + for (y = 0; y < ly; ++y) { + in = rin->pixels(y), end = in + lx, out = rout->pixels(y); + for (; in < end; ++in, ++out) + out->value = lut[in->value]; + } + + delete[] lut; +} + +//----------------------------------------------------------------------------- + +template +void do_greyAdjust(TRasterPT rout, TRasterPT rin, + const int in0, const int in1, const int out0, const int out1) +{ + typedef typename T::Channel Channel; + + assert(rout->getSize() == rin->getSize()); + + //Build scale parameters + double k = (out1 - out0) / (double)(in1 - in0); + double a = out0 - k * in0; + + do_greyScale_lut(rout, rin, a, k, out0, out1); +} + +//----------------------------------------------------------------------------- + +template +void do_rgbmScale_lut(TRasterPT rout, TRasterPT rin, + const double *a, const double *k, + const int *out0, const int *out1) +{ + assert(rout->getSize() == rin->getSize()); + + typedef typename T::Channel Channel; + int m, max = T::maxChannelValue, chanValuesCount = max + 1; + + int fac = chanValuesCount / 256; + int out0R = tmax(fac * out0[0], 0), out1R = tmin(fac * out1[0], T::maxChannelValue); + int out0G = tmax(fac * out0[1], 0), out1G = tmin(fac * out1[1], T::maxChannelValue); + int out0B = tmax(fac * out0[2], 0), out1B = tmin(fac * out1[2], T::maxChannelValue); + int out0M = tmax(fac * out0[3], 0), out1M = tmin(fac * out1[3], T::maxChannelValue); + + //Build luts + Channel *lut_r = new Channel[chanValuesCount]; + buildLUT(lut_r, a[0], k[0], out0R, out1R); + + Channel *lut_g = new Channel[chanValuesCount]; + buildLUT(lut_g, a[1], k[1], out0G, out1G); + + Channel *lut_b = new Channel[chanValuesCount]; + buildLUT(lut_b, a[2], k[2], out0B, out1B); + + Channel *lut_m = new Channel[chanValuesCount]; + buildLUT(lut_m, a[3], k[3], out0M, out1M); + + //Retrieve de/premultiplication luts + const double *lut_prem = premultiplyTable(); + const double *lut_deprem = depremultiplyTable(); + double premFac, depremFac; + + //Process raster + int y, lx = rin->getLx(), ly = rin->getLy(); + T *in, *end, *out; + + for (y = 0; y < ly; ++y) { + in = rin->pixels(y), end = in + lx, out = rout->pixels(y); + for (; in < end; ++in, ++out) { + m = lut_m[in->m]; + depremFac = lut_deprem[in->m]; + premFac = lut_prem[m]; + + out->r = premFac * lut_r[tmin((int)(in->r * depremFac), max)]; + out->g = premFac * lut_g[tmin((int)(in->g * depremFac), max)]; + out->b = premFac * lut_b[tmin((int)(in->b * depremFac), max)]; + out->m = m; + } + } + + delete[] lut_r; + delete[] lut_g; + delete[] lut_b; + delete[] lut_m; +} + +//----------------------------------------------------------------------------- + +template +void do_rgbmScale(TRasterPT rout, TRasterPT rin, + const double *a, const double *k, + const int *out0, const int *out1) +{ + assert(rout->getSize() == rin->getSize()); + + typedef typename T::Channel Channel; + int m, chanValuesCount = T::maxChannelValue + 1; + + int fac = chanValuesCount / 256; + + int out0R = tmax(fac * out0[0], 0), out1R = tmin(fac * out1[0], T::maxChannelValue); + int out0G = tmax(fac * out0[1], 0), out1G = tmin(fac * out1[1], T::maxChannelValue); + int out0B = tmax(fac * out0[2], 0), out1B = tmin(fac * out1[2], T::maxChannelValue); + int out0M = tmax(fac * out0[3], 0), out1M = tmin(fac * out1[3], T::maxChannelValue); + + //Retrieve de/premultiplication luts + const double *lut_prem = premultiplyTable(); + const double *lut_deprem = depremultiplyTable(); + double premFac, depremFac; + + //Process raster + int y, lx = rin->getLx(), ly = rin->getLy(); + T *in, *end, *out; + + for (y = 0; y < ly; ++y) { + in = rin->pixels(y), end = in + lx, out = rout->pixels(y); + for (; in < end; ++in, ++out) { + m = tcrop((int)(a[3] + k[3] * in->m), out0M, out1M); + depremFac = lut_deprem[in->m]; + premFac = lut_prem[m]; + + out->r = premFac * tcrop((int)(a[0] + k[0] * in->r * depremFac), out0R, out1R); + out->g = premFac * tcrop((int)(a[1] + k[1] * in->g * depremFac), out0G, out1G); + out->b = premFac * tcrop((int)(a[2] + k[2] * in->b * depremFac), out0B, out1B); + out->m = m; + } + } +} + +//----------------------------------------------------------------------------- + +template +void do_rgbmAdjust(TRasterPT rout, TRasterPT rin, ScaleFunc scaleFunc, + const int *in0, const int *in1, const int *out0, const int *out1) +{ + assert(rout->getSize() == rin->getSize()); + + double a[5], k[5]; + + //Build scale parameters + int i; + for (i = 0; i < 5; ++i) { + k[i] = (out1[i] - out0[i]) / (double)(in1[i] - in0[i]); + a[i] = out0[i] - k[i] * in0[i]; + } + for (i = 1; i < 4; ++i) { + a[i] += k[i] * a[0]; + k[i] *= k[0]; + } + + //Ensure that the output is cropped according to output params + int out0i[4], out1i[4]; + + out0i[0] = tmax(out0[0], tcrop((int)(a[0] + k[0] * out0[1]), 0, 255)); + out1i[0] = tmin(out1[0], tcrop((int)(a[0] + k[0] * out1[1]), 0, 255)); + + out0i[1] = tmax(out0[0], tcrop((int)(a[0] + k[0] * out0[2]), 0, 255)); + out1i[1] = tmin(out1[0], tcrop((int)(a[0] + k[0] * out1[2]), 0, 255)); + + out0i[2] = tmax(out0[0], tcrop((int)(a[0] + k[0] * out0[3]), 0, 255)); + out1i[2] = tmin(out1[0], tcrop((int)(a[0] + k[0] * out1[3]), 0, 255)); + + out0i[3] = out0[4]; + out1i[3] = out1[4]; + + scaleFunc(rout, rin, &a[1], &k[1], out0i, out1i); +} + +} //namespace + +//----------------------------------------------------------------------------- + +void TRop::rgbmScale(TRasterP rout, TRasterP rin, + const double *k, const double *a, const int *out0, const int *out1) +{ + if (rout->getSize() != rin->getSize()) + throw TRopException("size mismatch"); + + rout->lock(); + rin->lock(); + + if ((TRaster32P)rout && (TRaster32P)rin) + do_rgbmScale_lut(rout, rin, a, k, out0, out1); + else if ((TRaster64P)rout && (TRaster64P)rin) { + if (rin->getLx() * rin->getLy() < TPixel64::maxChannelValue) + do_rgbmScale(rout, rin, a, k, out0, out1); + else + do_rgbmScale_lut(rout, rin, a, k, out0, out1); + } else if ((TRasterGR8P)rout && (TRasterGR8P)rin) + do_greyScale_lut(rout, rin, a[0], k[0], out0[0], out1[0]); + else if ((TRasterGR16P)rout && (TRasterGR16P)rin) + do_greyScale_lut(rout, rin, a[0], k[0], out0[0], out1[0]); + else { + rout->unlock(); + rin->unlock(); + throw TRopException("pixel type mismatch"); + } + + rout->unlock(); + rin->unlock(); +} + +//----------------------------------------------------------------------------- + +void TRop::rgbmScale(TRasterP rout, TRasterP rin, + double kr, double kg, double kb, double km, + double ar, double ag, double ab, double am) +{ + double k[4], a[4]; + + a[0] = ar, a[1] = ag, a[2] = ab, a[3] = am; + k[0] = kr, k[1] = kg, k[2] = kb, k[3] = km; + + int out0[4], out1[4]; + + out0[0] = out0[1] = out0[2] = out0[3] = 0; + out1[0] = out1[1] = out1[2] = out1[3] = 255; + + rgbmScale(rout, rin, k, a, out0, out1); +} + +//----------------------------------------------------------------------------- + +void TRop::rgbmAdjust(TRasterP rout, TRasterP rin, + const int *in0, const int *in1, const int *out0, const int *out1) +{ + if (rout->getSize() != rin->getSize()) + throw TRopException("size mismatch"); + + rout->lock(); + rin->lock(); + + if ((TRaster32P)rout && (TRaster32P)rin) + do_rgbmAdjust(rout, rin, &do_rgbmScale_lut, in0, in1, out0, out1); + else if ((TRaster64P)rout && (TRaster64P)rin) { + if (rin->getLx() * rin->getLy() < TPixel64::maxChannelValue) + do_rgbmAdjust(rout, rin, &do_rgbmScale, in0, in1, out0, out1); + else + do_rgbmAdjust(rout, rin, &do_rgbmScale_lut, in0, in1, out0, out1); + } else if ((TRasterGR8P)rout && (TRasterGR8P)rin) + do_greyAdjust(rout, rin, in0[0], in1[0], out0[0], out1[0]); + else if ((TRasterGR16P)rout && (TRasterGR16P)rin) + do_greyAdjust(rout, rin, in0[0], in1[0], out0[0], out1[0]); + else { + rout->unlock(); + rin->unlock(); + throw TRopException("pixel type mismatch"); + } + + rout->unlock(); + rin->unlock(); +} diff --git a/toonz/sources/common/trop/trop.cpp b/toonz/sources/common/trop/trop.cpp new file mode 100644 index 0000000..cc1c263 --- /dev/null +++ b/toonz/sources/common/trop/trop.cpp @@ -0,0 +1,429 @@ + + +#include "trop.h" +#include "tconvert.h" +//#include "trastercm.h" +#ifndef TNZCORE_LIGHT +#include "timagecache.h" +#include "ttile.h" +#include "trasterimage.h" +#include "ttoonzimage.h" +#endif + +TString TRopException::getMessage() const +{ + return toWideString(message); +} + +namespace +{ + +bool isOpaque32(TRaster32P &ras) +{ + ras->lock(); + UCHAR *m0 = &(ras->pixels()->m); + if (0 < m0[0] && m0[0] < 255) + return false; + + int wrap4 = ras->getWrap() * 4; + int lx4 = ras->getLx() * 4; + const UCHAR cm = *m0; + int nrows = ras->getLy(); + while (nrows-- > 0) { + UCHAR *m1 = m0 + lx4; + UCHAR *m = m0; + while (m < m1 && *m == cm) + m += 4; + if (m < m1) + break; + m0 += wrap4; + } + ras->unlock(); + return (nrows <= 0); + + //m_image->setOpaqueFlag(true); +} + +} //namespace + +bool TRop::isOpaque(TRasterP ras) +{ + TRaster32P ras32 = ras; + if (ras32) + return isOpaque32(ras32); + else if (TRasterGR8P(ras)) + return true; + else + throw TRopException("isOpaque: unsupported pixel type"); +} + +#ifdef TNZ_MACHINE_CHANNEL_ORDER_MRGB +void TRop::swapRBChannels(const TRaster32P &r) +{ + int lx = r->getLx(); + int y = r->getLy(); + r->lock(); + while (--y >= 0) { + TPixel32 *pix = r->pixels(y); + TPixel32 *endPix = pix + lx; + while (pix < endPix) { + tswap(pix->r, pix->b); + ++pix; + } + } + r->unlock(); +} +#endif + +TRaster32P TRop::copyAndSwapRBChannels(const TRaster32P &srcRaster) +{ + TRaster32P newRaster(srcRaster->getSize()); + int lx = srcRaster->getLx(); + int y = srcRaster->getLy(); + srcRaster->lock(); + newRaster->lock(); + while (--y >= 0) { + TPixel32 *pix = srcRaster->pixels(y); + TPixel32 *newpix = newRaster->pixels(y); + TPixel32 *endPix = pix + lx; + while (pix < endPix) { + newpix->r = pix->b; + newpix->g = pix->g; + newpix->b = pix->r; + newpix->m = pix->m; + ++pix; + ++newpix; + } + } + srcRaster->unlock(); + newRaster->unlock(); + + return newRaster; +} + +void TRop::copy(TRasterP dst, const TRasterP &src) +{ + assert(!((TRasterCM32P)src) || (TRasterCM32P)dst); + if (dst->getPixelSize() == src->getPixelSize()) + dst->copy(src); + else { + if (dst->getBounds() != src->getBounds()) { + TRect rect = dst->getBounds() * src->getBounds(); + if (rect.isEmpty()) + return; + TRop::convert(dst->extract(rect), src->extract(rect)); + } else + TRop::convert(dst, src); + } +} + +//------------------------------------------------------------------- + +namespace +{ +template +class Gamma_Lut +{ + +public: + vector m_table; + Gamma_Lut(int insteps, int outsteps, double gamma) + { + double inspace = (double)(insteps); + for (int i = 0; i <= insteps; i++) + m_table.push_back((Q)((outsteps) * + (pow(i / inspace, 1.0 / gamma)) + + 0.5)); + } +}; + +template +void doGammaCorrect(TRasterPT raster, double gamma) +{ + Gamma_Lut lut(T::maxChannelValue, T::maxChannelValue, gamma); + + int j; + for (j = 0; j < raster->getLy(); j++) { + T *pix = raster->pixels(j); + T *endPix = pix + raster->getLx(); + while (pix < endPix) { + pix->r = lut.m_table[pix->r]; + pix->b = lut.m_table[pix->b]; + pix->g = lut.m_table[pix->g]; + /*if(pix->m != T::maxChannelValue) + { + pix->r= pix->r*pix->m/T::maxChannelValue; + pix->g= pix->g*pix->m/T::maxChannelValue; + pix->b= pix->b*pix->m/T::maxChannelValue; + }*/ + *pix++; + } + } +} +template +void doGammaCorrectRGBM(TRasterPT raster, double gammar, double gammag, double gammab, double gammam) +{ + Gamma_Lut lutr(T::maxChannelValue, T::maxChannelValue, gammar); + Gamma_Lut lutg(T::maxChannelValue, T::maxChannelValue, gammag); + Gamma_Lut lutb(T::maxChannelValue, T::maxChannelValue, gammab); + Gamma_Lut lutm(T::maxChannelValue, T::maxChannelValue, gammam); + int j; + for (j = 0; j < raster->getLy(); j++) { + T *pix = raster->pixels(j); + T *endPix = pix + raster->getLx(); + while (pix < endPix) { + pix->r = lutr.m_table[pix->r]; + pix->g = lutg.m_table[pix->g]; + pix->b = lutb.m_table[pix->b]; + pix->m = lutm.m_table[pix->m]; + /*if(pix->m != T::maxChannelValue) + { + pix->r= pix->r*pix->m/T::maxChannelValue; + pix->g= pix->g*pix->m/T::maxChannelValue; + pix->b= pix->b*pix->m/T::maxChannelValue; + }*/ + *pix++; + } + } +} +} +//------------------------------------------------------------------- + +void TRop::gammaCorrect(TRasterP raster, double gamma) +{ + if (gamma <= 0) + gamma = 0.01; + raster->lock(); + + if ((TRaster32P)raster) + doGammaCorrect(raster, gamma); + else if ((TRaster64P)raster) + doGammaCorrect(raster, gamma); + else { + raster->unlock(); + throw TRopException("isOpaque: unsupported pixel type"); + } + raster->unlock(); +} +//------------------------------------------------------------------- + +void TRop::gammaCorrectRGBM(TRasterP raster, double gammar, double gammag, + double gammab, double gammam) +{ + if (gammar <= 0) + gammar = 0.01; + if (gammag <= 0) + gammag = 0.01; + if (gammab <= 0) + gammab = 0.01; + if (gammam <= 0) + gammam = 0.01; + + raster->lock(); + + if ((TRaster32P)raster) + doGammaCorrectRGBM(raster, gammar, gammag, gammab, gammam); + else if ((TRaster64P)raster) + doGammaCorrectRGBM(raster, gammar, gammag, gammab, gammam); + else { + raster->unlock(); + throw TRopException("isOpaque: unsupported pixel type"); + } + raster->unlock(); +} +//------------------------------------------------------------------- + +template +void doSetChannel(const TRasterPT &rin, const TRasterPT &rout, UCHAR channel, bool greytones) +{ + + int lx = rin->getLx(); + int ly = rout->getLy(); + + int i, j; + for (i = 0; i < ly; i++) { + T *pixin = rin->pixels(i); + T *pixout = rout->pixels(i); + if (greytones || channel == TRop::MChan) { + switch (channel) { + case TRop::RChan: + for (j = 0; j < lx; j++, pixin++, pixout++) + pixout->r = pixout->g = pixout->b = pixout->m = pixin->r; + break; + case TRop::GChan: + for (j = 0; j < lx; j++, pixin++, pixout++) + pixout->r = pixout->g = pixout->b = pixout->m = pixin->g; + break; + case TRop::BChan: + for (j = 0; j < lx; j++, pixin++, pixout++) + pixout->r = pixout->g = pixout->b = pixout->m = pixin->b; + break; + case TRop::MChan: + for (j = 0; j < lx; j++, pixin++, pixout++) + pixout->r = pixout->g = pixout->b = pixout->m = pixin->m; + break; + default: + assert(false); + } + } else { + for (j = 0; j < lx; j++, pixin++, pixout++) { + pixout->r = channel & TRop::RChan ? pixin->r : 0; + pixout->b = channel & TRop::BChan ? pixin->b : 0; + pixout->g = channel & TRop::GChan ? pixin->g : 0; + } + } + } +} + +//------------------------------------------------------------------- + +void TRop::setChannel(const TRasterP &rin, TRasterP rout, UCHAR chan, bool greytones) +{ + assert(rin->getSize() == rout->getSize()); + + rout->lock(); + + if ((TRaster32P)rin && (TRaster32P)rout) + doSetChannel(rin, rout, chan, greytones); + else if ((TRaster64P)rin && (TRaster64P)rout) + doSetChannel(rin, rout, chan, greytones); + else { + rout->unlock(); + throw TRopException("setChannel: unsupported pixel type"); + } + + rout->unlock(); +} + +//------------------------------------------------------------------- + +TRasterP TRop::shrink(TRasterP rin, int shrink) +{ + int pixelSize = rin->getPixelSize(); + + int lx = (rin->getLx() - 1) / shrink + 1; + int ly = (rin->getLy() - 1) / shrink + 1; + + TRasterP rout; + + if ((TRaster32P)rin) + rout = TRaster32P(lx, ly); + else if ((TRaster64P)rin) + rout = TRaster64P(lx, ly); + if ((TRasterCM32P)rin) + rout = TRasterCM32P(lx, ly); + if ((TRasterGR8P)rin) + rout = TRasterGR8P(lx, ly); + + int i, j; + + for (i = 0; i < ly; i++) { + UCHAR *bufin = (UCHAR *)rin->getRawData() + (i * shrink) * rin->getWrap() * pixelSize; + UCHAR *bufout = (UCHAR *)rout->getRawData() + i * rout->getWrap() * pixelSize; + for (j = 0; j < lx; j++) { + memcpy(bufout, bufin, pixelSize); + bufin += shrink * pixelSize; + bufout += pixelSize; + } + } + return rout; +} + +//------------------------------------------------------------------- + +template +void doMakeStereoRaster(const TRasterPT &rleft, const TRasterPT &rright) +{ + + int lx = rleft->getLx(); + int ly = rright->getLy(); + + for (int i = 0; i < ly; i++) { + T *pixl = rleft->pixels(i); + T *pixr = rright->pixels(i); + + for (int j = 0; j < lx; j++, pixl++, pixr++) { + pixl->g = pixr->g; + pixl->b = pixr->b; + } + } +} + +//--------------------------------------- + +void TRop::makeStereoRaster(const TRasterP &left, const TRasterP &right) +{ + assert(left->getSize() == right->getSize()); + + left->lock(); + + if ((TRaster32P)left && (TRaster32P)right) + doMakeStereoRaster(left, right); + else if ((TRaster64P)left && (TRaster64P)right) + doMakeStereoRaster(left, right); + else { + left->unlock(); + throw TRopException("setChannel: unsupported pixel type"); + } + + left->unlock(); +} + +//------------------------------------------------------------------- +#ifndef TNZCORE_LIGHT + +void TTile::addInCache(const TRasterP &raster) +{ + if (!raster) { + m_rasterId = ""; + return; + } + TRasterP rin; + + m_rasterId = TImageCache::instance()->getUniqueId(); + if (raster->getParent()) { + rin = raster->getParent(); + unsigned long offs = (raster->getRawData() - raster->getParent()->getRawData()) / raster->getPixelSize(); + m_subRect = TRect(TPoint(offs % raster->getWrap(), offs / raster->getWrap()), raster->getSize()); + } else { + m_subRect = raster->getBounds(); + rin = raster; + } + + if ((TRasterCM32P)rin) + TImageCache::instance()->add(m_rasterId, TToonzImageP(rin, rin->getBounds())); + else if ((TRaster32P)rin || (TRaster64P)rin) + TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); + else if ((TRasterGR8P)rin || (TRasterGR16P)rin) + TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); + else + assert(false); +} + +TTile::TTile(const TRasterP &raster) + : m_pos(), m_subRect() +{ + addInCache(raster); +} + +TTile::TTile(const TRasterP &raster, TPointD pos) + : m_pos(pos), m_subRect() +{ + addInCache(raster); +} + +void TTile::setRaster(const TRasterP &raster) +{ + if (m_rasterId != "") + TImageCache::instance()->remove(m_rasterId); + m_subRect = TRect(); + addInCache(raster); +} + +TTile::~TTile() +{ + if (!m_rasterId.empty()) + TImageCache::instance()->remove(m_rasterId); +} + +#endif diff --git a/toonz/sources/common/trop/trop_borders.cpp b/toonz/sources/common/trop/trop_borders.cpp new file mode 100644 index 0000000..4332afd --- /dev/null +++ b/toonz/sources/common/trop/trop_borders.cpp @@ -0,0 +1,359 @@ + + +// Local includes +#include "pixelselectors.h" +#include "runsmap.h" + +// STD includes +#include + +// tcg includes +#include "tcg/tcg_traits.h" +#include "tcg/tcg_vertex.h" +#include "tcg/tcg_edge.h" +#include "tcg/tcg_face.h" + +#define INCLUDE_HPP +#include "tcg/tcg_mesh.h" +#include "raster_edge_iterator.h" +#include "borders_extractor.h" +#undef INCLUDE_HPP + +#include "trop_borders.h" + +using namespace TRop::borders; + +namespace +{ + +//**************************************************************** +// Container Reader for Borders Reader +//**************************************************************** + +template +class WrapperReader +{ + TRop::borders::BordersReader &m_reader; + +public: + WrapperReader(TRop::borders::BordersReader &reader) + : m_reader(reader) {} + + void openContainer(const RasterEdgeIterator &it) + { + m_reader.openContainer(it.pos(), it.dir(), it.rightColor(), it.leftColor()); + } + void addElement(const RasterEdgeIterator &it) + { + m_reader.addElement(it.pos(), it.dir(), it.leftColor()); + } + void closeContainer() { m_reader.closeContainer(); } +}; + +} //namespace + +//**************************************************************** +// Borders Extractor instantiations +//**************************************************************** + +namespace TRop +{ +namespace borders +{ + +template +void readBorders_simple(const TRasterPT &raster, BordersReader &reader, bool onlyCorners) +{ + typedef PixelSelector pixel_selector; + + pixel_selector selector(onlyCorners); + WrapperReader wrapReader(reader); + + raster->lock(); + readBorders(raster, selector, wrapReader); + raster->unlock(); +} + +//-------------------------------------------------------------------------------- + +void readBorders_simple(const TRasterGR8P &raster, BordersReader &reader, + const TPixelGR8 &transparencyColor, bool onlyCorners) +{ + typedef PixelSelector pixel_selector; + + pixel_selector selector(onlyCorners, transparencyColor); + WrapperReader wrapReader(reader); + + raster->lock(); + readBorders(raster, selector, wrapReader); + raster->unlock(); +} + +//-------------------------------------------------------------------------------- + +void readBorders_simple(const TRasterGR16P &raster, BordersReader &reader, + const TPixelGR16 &transparencyColor, bool onlyCorners) +{ + typedef PixelSelector pixel_selector; + + pixel_selector selector(onlyCorners, transparencyColor); + WrapperReader wrapReader(reader); + + raster->lock(); + readBorders(raster, selector, wrapReader); + raster->unlock(); +} + +//-------------------------------------------------------------------------------- + +void readBorders_simple(const TRasterCM32P &raster, BordersReader &reader, + bool onlyCorners, int toneThreshold) +{ + typedef PixelSelector pixel_selector; + + pixel_selector selector(onlyCorners, toneThreshold); + WrapperReader wrapReader(reader); + + raster->lock(); + readBorders(raster, selector, wrapReader); + raster->unlock(); +} + +//-------------------------------------------------------------------------------- + +void readBorders_simple(const TRasterP &raster, BordersReader &reader, bool onlyCorners) +{ + TRaster32P ras32(raster); + if (ras32) { + readBorders_simple(ras32, reader, onlyCorners); + return; + } + + TRaster64P ras64(raster); + if (ras64) { + readBorders_simple(ras64, reader, onlyCorners); + return; + } + + TRasterCM32P rasCM32(raster); + if (rasCM32) { + readBorders_simple(rasCM32, reader, onlyCorners); + return; + } + + TRasterGR8P rasGR8(raster); + if (rasGR8) { + readBorders_simple(rasGR8, reader, onlyCorners); + return; + } + + TRasterGR16P rasGR16(raster); + if (rasGR16) { + readBorders_simple(rasGR16, reader, onlyCorners); + return; + } +} +} +} //namespace TRop::borders + +//**************************************************************** +// Meshes Extraction (MeshesReader::Imp) +//**************************************************************** + +namespace TRop +{ +namespace borders +{ + +class TRop::borders::ImageMeshesReader::Imp +{ +public: + Face m_outerFace; + tcg::list m_meshes; + + std::stack m_facesStack; + + int m_facesCount, m_edgesCount; + +public: + Imp() : m_facesCount(), m_edgesCount() {} + + void clear() + { + assert(m_facesStack.empty()); + + m_outerFace = Face(); + m_meshes.clear(); + m_facesCount = m_edgesCount = 0; + } +}; + +//**************************************************************** +// Meshes Extraction (MeshesReader) +//**************************************************************** + +ImageMeshesReader::ImageMeshesReader() + : m_imp(new Imp) +{ +} + +//-------------------------------------------------------------------------------- + +ImageMeshesReader::~ImageMeshesReader() +{ + delete m_imp; +} + +//-------------------------------------------------------------------------------- + +void ImageMeshesReader::clear() +{ + m_imp->clear(); +} + +//-------------------------------------------------------------------------------- + +const Face &ImageMeshesReader::outerFace() const +{ + return m_imp->m_outerFace; +} + +//-------------------------------------------------------------------------------- + +Face &ImageMeshesReader::outerFace() +{ + return m_imp->m_outerFace; +} + +//-------------------------------------------------------------------------------- + +const tcg::list &ImageMeshesReader::meshes() const +{ + return m_imp->m_meshes; +} + +//-------------------------------------------------------------------------------- + +tcg::list &ImageMeshesReader::meshes() +{ + return m_imp->m_meshes; +} + +//-------------------------------------------------------------------------------- + +void ImageMeshesReader::openFace(ImageMesh *mesh, int faceIdx) +{ + Face &fc = mesh ? mesh->face(faceIdx) : m_imp->m_outerFace; + fc.imageIndex() = m_imp->m_facesCount++; + m_imp->m_facesStack.push(&fc); +} + +//-------------------------------------------------------------------------------- + +void ImageMeshesReader::addMesh(ImageMesh *mesh) +{ + Face &fc = *m_imp->m_facesStack.top(); + fc.meshes().push_back(m_imp->m_meshes.push_back(mesh)); +} + +//-------------------------------------------------------------------------------- + +void ImageMeshesReader::closeFace() +{ + m_imp->m_facesStack.pop(); +} + +//-------------------------------------------------------------------------------- + +void ImageMeshesReader::closeEdge(ImageMesh *mesh, int edgeIdx) +{ + mesh->edge(edgeIdx).imageIndex() = m_imp->m_edgesCount++; +} +} +} // namespace TRop::borders + +//**************************************************************** +// Meshes Extraction (traits) +//**************************************************************** + +namespace tcg +{ + +using namespace TRop::borders; + +// Reader traits + +template +struct container_reader_traits, ImageMesh::face_type> { + typedef ImageMeshesReaderT meshes_reader_type; + typedef typename meshes_reader_type::value_type value_type; + +public: + inline static void openContainer(meshes_reader_type &reader, ImageMesh *mesh, int faceIdx, const value_type &colorValue) + { + reader.openFace(mesh, faceIdx, colorValue); + } + inline static void addElement(meshes_reader_type &reader, ImageMesh *mesh) { reader.addMesh(mesh); } + inline static void closeContainer(meshes_reader_type &reader) { reader.closeFace(); } +}; + +template +struct container_reader_traits, ImageMesh::edge_type> { + typedef ImageMeshesReaderT meshes_reader_type; + typedef typename meshes_reader_type::raster_edge_iterator raster_edge_iterator; + +public: + inline static void openContainer(meshes_reader_type &reader, const raster_edge_iterator &it) + { + reader.openEdge(it); + } + inline static void addElement(meshes_reader_type &reader, const raster_edge_iterator &it) + { + reader.addVertex(it); + } + inline static void closeContainer(meshes_reader_type &reader, ImageMesh *mesh, int edgeIdx) + { + reader.closeEdge(mesh, edgeIdx); + } +}; + +} //namespace tcg + +//**************************************************************** +// Meshes Extraction (functions) +//**************************************************************** + +namespace TRop +{ +namespace borders +{ + +template +void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader) +{ + typedef PixelSelector pixel_selector; + + reader.clear(); + + raster->lock(); + readMeshes>(raster, reader.pixelSelector(), reader); + raster->unlock(); +} + +//-------------------------------------------------------------- + +// Standard type instantiations + +template DV_EXPORT_API class RasterEdgeIterator>; +template DV_EXPORT_API class RasterEdgeIterator>; +template DV_EXPORT_API class RasterEdgeIterator>; +template DV_EXPORT_API class RasterEdgeIterator>; +template DV_EXPORT_API class RasterEdgeIterator>; + +template DV_EXPORT_API void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader); +template DV_EXPORT_API void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader); +template DV_EXPORT_API void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader); +template DV_EXPORT_API void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader); +template DV_EXPORT_API void readMeshes(const TRasterPT &raster, ImageMeshesReaderT &reader); +} +} //namespace TRop::borders diff --git a/toonz/sources/common/trop/tropcm.cpp b/toonz/sources/common/trop/tropcm.cpp new file mode 100644 index 0000000..8cfc930 --- /dev/null +++ b/toonz/sources/common/trop/tropcm.cpp @@ -0,0 +1,1387 @@ + + +#include "tsystem.h" + +#include + +#include "tropcm.h" + +#include "tcolorstyles.h" +#include "tpixelutils.h" +#include "ttile.h" +#include "tpalette.h" +#include "timage_io.h" +#include "trasterimage.h" + +//#include "tlevel.h" +//#include "ttoonzimage.h" +//#include "tgeometry.h" +//#include "timage_io.h" + +extern "C" { +#include "toonz4.6/raster.h" +} + +#ifdef WIN32 +#define USE_SSE2 +#endif + +#ifdef USE_SSE2 +#include + +//--------------------------------------------------------- + +namespace +{ + +__declspec(align(16)) class TPixelFloat +{ +public: + TPixelFloat() : b(0), g(0), r(0), m(0) {} + + TPixelFloat(float rr, float gg, float bb, float mm) + : b(bb), g(gg), r(rr), m(mm) {} + + TPixelFloat(const TPixel32 &pix) + : b(pix.b), g(pix.g), r(pix.r), m(pix.m) {} + + float b, g, r, m; +}; + +} // anonymous namespace + +#endif + +//----------------------------------------------------------------------------- + +bool renderRas32(const TTile &tileOut, const TTile &tileIn, const TPaletteP palette); + +//----------------------------------------------------------------------------- + +const TPixel32 c_transparencyCheckPaint = TPixel32(80, 80, 80, 255); +const TPixel32 c_transparencyCheckInk = TPixel32::Black; + +void TRop::convert(const TRaster32P &rasOut, + const TRasterCM32P &rasIn, + const TPaletteP palette, + bool transparencyCheck) +{ + int count = palette->getStyleCount(); + int count2 = tmax(count, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + + // per poter utilizzare lo switch (piu' efficiente) si utilizza 255 + // anziche' TPixelCM32::getMaxTone() + assert(TPixelCM32::getMaxTone() == 255); + + int rasLx = rasOut->getLx(); + int rasLy = rasOut->getLy(); + + rasOut->lock(); + rasIn->lock(); +#ifdef WIN32 + if (TSystem::getCPUExtensions() & TSystem::CpuSupportsSse2) { + __m128i zeros = _mm_setzero_si128(); + TPixelFloat *paints = (TPixelFloat *)_aligned_malloc(count2 * sizeof(TPixelFloat), 16); + TPixelFloat *inks = (TPixelFloat *)_aligned_malloc(count2 * sizeof(TPixelFloat), 16); + + std::vector paints2(count2); + std::vector inks2(count2); + if (transparencyCheck) { + for (int i = 0; i < palette->getStyleCount(); i++) { + paints2[i] = c_transparencyCheckPaint; + inks2[i] = c_transparencyCheckInk; + paints[i] = TPixelFloat(paints2[i]); + inks[i] = TPixelFloat(inks2[i]); + } + paints2[0] = TPixel32::Transparent; + paints[0] = TPixelFloat(TPixel32::Transparent); + } + /* + else if (true) + { + for(int i=0;igetStyleCount();i++) + { + paints2[i] = c_transparencyCheckPaint; + inks2[i] = c_transparencyCheckInk; + paints[i] = TPixelFloat(paints2[i]); + inks[i] = TPixelFloat(inks2[i]); + paints2[i] = TPixel32::Transparent; + paints[i] = TPixelFloat(TPixel32::Transparent); + } + paints2[0] = TPixel32::Transparent; + paints[0] = TPixelFloat(TPixel32::Transparent); + } + */ + else + for (int i = 0; i < palette->getStyleCount(); i++) { + TPixel32 color = ::premultiply(palette->getStyle(i)->getAverageColor()); + paints[i] = inks[i] = TPixelFloat(color); + paints2[i] = inks2[i] = color; + } + + float maxTone = (float)TPixelCM32::getMaxTone(); + __m128 den_packed = _mm_load1_ps(&maxTone); + + for (int y = 0; y < rasLy; ++y) { + TPixel32 *pix32 = rasOut->pixels(y); + TPixelCM32 *pixIn = rasIn->pixels(y); + + TPixelCM32 *endPixIn = pixIn + rasLx; + + while (pixIn < endPixIn) { + int tt = pixIn->getTone(); + int p = pixIn->getPaint(); + int i = pixIn->getInk(); + switch (tt) { + case 255: + *pix32++ = paints2[p]; + break; + case 0: + *pix32++ = inks2[i]; + break; + default: { + float t = (float)tt; + __m128 a_packed = _mm_load_ps((float *)&(inks[i])); + __m128 b_packed = _mm_load_ps((float *)&(paints[p])); + + __m128 num_packed = _mm_load1_ps(&t); + __m128 diff_packed = _mm_sub_ps(den_packed, num_packed); + + // calcola in modo vettoriale out = ((den-num)*a + num*b)/den + __m128 outPix_packed = _mm_mul_ps(diff_packed, a_packed); + __m128 tmpPix_packed = _mm_mul_ps(num_packed, b_packed); + + outPix_packed = _mm_add_ps(outPix_packed, tmpPix_packed); + outPix_packed = _mm_div_ps(outPix_packed, den_packed); + + // converte i canali da float a char + __m128i outPix_packed_i = _mm_cvtps_epi32(outPix_packed); + outPix_packed_i = _mm_packs_epi32(outPix_packed_i, zeros); + outPix_packed_i = _mm_packus_epi16(outPix_packed_i, zeros); + + *(DWORD *)(pix32) = _mm_cvtsi128_si32(outPix_packed_i); + ++pix32; + } + } + ++pixIn; + } + } + + _aligned_free(paints); + _aligned_free(inks); + + } else // SSE2 not supported +#endif // WIN32 + { + + std::vector paints(count2, TPixel32(255, 0, 0)); + std::vector inks(count2, TPixel32(255, 0, 0)); + if (transparencyCheck) { + for (int i = 0; i < palette->getStyleCount(); i++) { + paints[i] = c_transparencyCheckPaint; + inks[i] = c_transparencyCheckInk; + } + paints[0] = TPixel32::Transparent; + } else + for (int i = 0; i < palette->getStyleCount(); i++) + paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); + + for (int y = 0; y < rasLy; ++y) { + TPixel32 *pix32 = rasOut->pixels(y); + TPixelCM32 *pixIn = rasIn->pixels(y); + TPixelCM32 *endPixIn = pixIn + rasLx; + + while (pixIn < endPixIn) { + int t = pixIn->getTone(); + int p = pixIn->getPaint(); + int i = pixIn->getInk(); + + if (t == TPixelCM32::getMaxTone()) + *pix32++ = paints[p]; + else if (t == 0) + *pix32++ = inks[i]; + else + *pix32++ = blend(inks[i], paints[p], t, TPixelCM32::getMaxTone()); + + ++pixIn; + } + } + } + rasOut->unlock(); + rasIn->unlock(); +} + +//----------------------------------------------------------------------------------------------- + +void do_convert(const TTile &dst, const TTile &src, + const TPaletteP palette, bool transparencyCheck, + bool applyFx) +{ + + //assert(palette); + //assert(_rasOut && _rasIn); + //assert(rasOut->getSize() == rasIn->getSize()); + + if (applyFx && renderRas32(dst, src, palette)) + return; + + TRaster32P rasOut; + TRasterCM32P rasIn; + + if (dst.getRaster()->getSize() != src.getRaster()->getSize()) { + TRect rect = TRect(convert(dst.m_pos), dst.getRaster()->getSize()) * TRect(convert(src.m_pos), src.getRaster()->getSize()); + TRect rectOut = rect - convert(dst.m_pos); + rasOut = dst.getRaster()->extract(rectOut); + TRect rectIn = rect - convert(src.m_pos); + rasIn = src.getRaster()->extract(rectIn); + } else { + rasOut = dst.getRaster(); + rasIn = src.getRaster(); + } + TRop::convert(rasOut, rasIn, palette, transparencyCheck); +} + +//----------------------------------------------------------------------------------------------- + +void TRop::convert(const TRaster32P &rasOut, + const TRasterCM32P &rasIn, + TPaletteP palette, + const TRect &theClipRect, // il rect su cui e' applicata la conversione + bool transparencyCheck, + bool applyFx) +{ + assert(palette); + assert(rasIn && rasOut); + + TRect clipRect(theClipRect); + if (clipRect.isEmpty()) + clipRect = rasIn->getBounds(); + else { + if (!clipRect.overlaps(rasIn->getBounds())) + return; + clipRect = clipRect * rasIn->getBounds(); + } + if (clipRect.isEmpty()) + return; + + TRect clipRectIn, clipRectOut; + + if (applyFx && palette->getFxRects(clipRect, clipRectIn, clipRectOut)) { + TRect rAux = clipRectIn; + TRasterP rAuxIn = rasIn->extract(clipRectIn); //la extract modifica clipRectIn + if (rAux != clipRectIn && rAux != rasIn->getBounds()) { + TRasterCM32P rNew(rAux.getSize()); + TRect tmpRect = clipRectIn - rAux.getP00(); + rNew->extract(tmpRect)->copy(rAuxIn); + rAuxIn = rNew; + clipRectIn = rAux; + } + + TTile tileIn(rAuxIn, ::convert(clipRectIn.getP00())); + + rAux = clipRectOut; + TRasterP rAuxOut = rasOut->extract(clipRectOut); //la extract modifica clipRectOut + TTile tileOut(rAuxOut, ::convert(clipRectOut.getP00())); + TRop::convert(tileOut, tileIn, palette, transparencyCheck, true); + } else { + TRect clipRectIn = clipRect; + TRect clipRectOut = clipRect; + TRasterP _rasOut = rasOut->extract(clipRectOut); + TRasterP _rasIn = rasIn->extract(clipRectIn); + + TTile t1(_rasOut, ::convert(clipRectOut.getP00())); + TTile t2(_rasIn, ::convert(clipRectIn.getP00())); + + TRop::convert(t1, t2, + palette, transparencyCheck, false); + } +} + +//----------------------------------------------------------------------------- + +void TRop::convert(const TTile &dst, const TTile &src, const TPaletteP plt, bool transparencyCheck, bool applyFxs) +{ + //if (dst->getSize() != src->getSize()) + // throw TRopException("convert: size mismatch"); + + //assert(plt); + if ((TRaster32P)dst.getRaster()) + do_convert(dst, src, plt, transparencyCheck, applyFxs); + else if ((TRaster64P)dst.getRaster()) { + TRaster32P aux(dst.getRaster()->getLx(), dst.getRaster()->getLy()); + TTile taux(aux, dst.m_pos); + do_convert(taux, src, plt, transparencyCheck, applyFxs); + TRop::convert(dst.getRaster(), aux); + } else + throw TRopException("unsupported pixel type"); +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +namespace +{ + +TRasterP putSinglePaintInRaster(TRasterCM32P &rasIn, int paintId, TPixel32 color) +{ + TRaster32P rasOut; + + for (int y = 0; y < rasIn->getLy(); y++) { + TPixelCM32 *pixIn = rasIn->pixels(y); + TPixelCM32 *endPix = pixIn + rasIn->getLx(); + TPixel32 *pixOut = 0; + + if (rasOut) + pixOut = rasOut->pixels(y); + + while (pixIn < endPix) { + if (pixIn->getPaint() == paintId) { + if (!rasOut) { + rasOut = TRaster32P(rasIn->getLx(), rasIn->getLy()); + rasOut->fill(TPixel32::Transparent); + pixOut = rasOut->pixels(y) + (pixIn - rasIn->pixels(y)); + } + + if (!pixIn->isPureInk()) + *pixOut = color; + /*else if (!pixIn->isPureInk()) + { + assert(TPixelCM32::getMaxTone()==0xff); + pixOut->m = pixIn->getTone(); + pixOut->r = 255*color.r/pixOut->m; + pixOut->g = 255*color.g/pixOut->m; + pixOut->b = 255*color.b/pixOut->m; + }*/ + pixIn->setPaint(0); + //pixIn->setTone(0); + } + pixIn++; + pixOut++; + } + } + return rasOut; +} + +/*------------------------------------------------------------------------*/ + +TRasterP putSingleInkInRasterGR8(TRasterCM32P &rasIn, int inkId) +{ + TRasterGR8P rasOut; + + for (int y = 0; y < rasIn->getLy(); y++) { + TPixelCM32 *pixIn = rasIn->pixels(y); + TPixelCM32 *endPix = pixIn + rasIn->getLx(); + TPixelGR8 *pixOut; + + if (rasOut) + pixOut = rasOut->pixels(y); + + while (pixIn < endPix) { + if (pixIn->getInk() == inkId) { + assert(TPixelCM32::getMaxTone() == 0xff); + if (!rasOut) { + rasOut = TRasterGR8P(rasIn->getLx(), rasIn->getLy()); + rasOut->fill(0); + pixOut = rasOut->pixels(y) + (pixIn - rasIn->pixels(y)); + } + + *pixOut = 255 - pixIn->getTone(); + //pixIn->setInk(0); + pixIn->setTone(TPixelCM32::getMaxTone()); + } + + pixIn++; + pixOut++; + } + } + return rasOut; +} + +/*------------------------------------------------------------------------*/ +TRasterP putSingleInkInRasterRGBM(TRasterCM32P &rasIn, int inkId) +{ + TRaster32P rasOut; + + for (int y = 0; y < rasIn->getLy(); y++) { + TPixelCM32 *pixIn = rasIn->pixels(y); + TPixelCM32 *endPix = pixIn + rasIn->getLx(); + TPixel *pixOut; + + if (rasOut) + pixOut = rasOut->pixels(y); + + while (pixIn < endPix) { + if (pixIn->getInk() == inkId) { + assert(TPixelCM32::getMaxTone() == 0xff); + if (!rasOut) { + rasOut = TRaster32P(rasIn->getLx(), rasIn->getLy()); + rasOut->fill(TPixel32::Transparent); + pixOut = rasOut->pixels(y) + (pixIn - rasIn->pixels(y)); + } + + pixOut->r = pixOut->g = pixOut->b = pixOut->m = 255 - pixIn->getTone(); + //pixIn->setInk(0); + pixIn->setTone(TPixelCM32::getMaxTone()); + } + pixIn++; + if (rasOut) + pixOut++; + } + } + return rasOut; +} + +/*------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ + +//filename!! +//interactive!! + +bool computePaletteFx(const vector> &fx, + const TTile &tileOut, const TTile &tileIn, const TPaletteP plt) +{ + int i; + TRasterCM32P rasIn = tileIn.getRaster(); + TRaster32P rAux32, rasOut = tileOut.getRaster(); + TRasterGR8P rAuxGR; + + int frame = plt->getFrame(); + + vector paintLayers(fx.size()); + vector inkLayers(fx.size()); + + //tolgo dal raster d'ingresso gli ink e i paint con gli effetti, mettendoli in dei raster layer + for (i = 0; i < (int)fx.size(); i++) { + TRasterStyleFx *rfx = fx[i].first->getRasterStyleFx(); + + if (rfx->isPaintStyle()) + paintLayers[i] = putSinglePaintInRaster(rasIn, fx[i].second, fx[i].first->getMainColor()); + if (rfx->isInkStyle()) { + if (rfx->inkFxNeedRGBMRaster()) + inkLayers[i] = putSingleInkInRasterRGBM(rasIn, fx[i].second); + else + inkLayers[i] = putSingleInkInRasterGR8(rasIn, fx[i].second); + } + } + + //raster d'ingresso senza i colori fx, viene renderizzato nel raster d'uscita + + do_convert(tileOut, tileIn, plt, false, false); + + //ogni layer viene "effettato".; se il risultato e' non nullo, viene sovrapposto sul raster d'uscita + //prima vengono messi tutti i layer di paint, poi quelli di ink + + TRect rectOut = TRect(convert(tileOut.m_pos), tileOut.getRaster()->getSize()) - convert(tileIn.m_pos); + + for (i = 0; i < (int)fx.size(); i++) + if (paintLayers[i]) { + TRasterStyleFx::Params params(paintLayers[i], convert(tileIn.m_pos), rasIn, fx[i].second, frame); + if (fx[i].first->getRasterStyleFx()->compute(params)) + TRop::over(rasOut, paintLayers[i]->extract(rectOut), rasOut); + } + + for (i = 0; i < (int)fx.size(); i++) + if (inkLayers[i]) { + TRasterStyleFx *rfx = fx[i].first->getRasterStyleFx(); + + TRasterStyleFx::Params params(inkLayers[i], convert(tileIn.m_pos), rasIn, fx[i].second, frame); + if (rfx->compute(params)) { + if (rfx->inkFxNeedRGBMRaster()) + TRop::over(rasOut, rasOut, inkLayers[i]->extract(rectOut)); + else + TRop::over(rasOut, inkLayers[i]->extract(rectOut), fx[i].first->getMainColor()); + } + } + + return true; +} + +/*------------------------------------------------------------------------*/ + +} //namespace + +/*------------------------------------------------------------------------*/ + +bool renderRas32(const TTile &tileOut, const TTile &tileIn, const TPaletteP palette) +{ + assert(TRect(convert(tileIn.m_pos), tileIn.getRaster()->getSize()).contains(TRect(convert(tileOut.m_pos), tileOut.getRaster()->getSize()))); + assert((TRasterCM32P)tileIn.getRaster()); + + //Shrink = shrink; + //INIT_TCM(rin) + + /* mark up are made apart */ + //computeMarkup(rasIn, palette); + + vector> fxArray; + + for (int i = 0; i < palette->getStyleCount(); i++) + if (palette->getStyle(i)->isRasterStyle()) + fxArray.push_back(pair(palette->getStyle(i), i)); + + if (fxArray.empty()) + return false; + + TTile _tileIn(tileIn.getRaster()->clone(), tileIn.m_pos); + + tileIn.getRaster()->lock(); + tileOut.getRaster()->lock(); + computePaletteFx(fxArray, tileOut, _tileIn, palette); + tileIn.getRaster()->unlock(); + tileOut.getRaster()->unlock(); + + return true; +} + +/*------------------------------------------------------------------------*/ + +//-------------------------------------------------------------------------------------------- + +namespace +{ + +void addColor(TPaletteP plt, int index, const TPaletteP &upPlt, std::map &usedInks) +{ + TColorStyle *cs = upPlt->getStyle(index); + if (cs && cs->getMainColor() == plt->getStyle(index)->getMainColor()) { + usedInks[index] = index; + return; + } + int firstStyleId = plt->getFirstUnpagedStyle(); + if (firstStyleId == -1) + firstStyleId = plt->getStyleCount(); + usedInks[index] = firstStyleId; + plt->getPage(0)->addStyle(TPixel32::Red); +} + +void addColor(TPaletteP plt, int index, std::map &usedInks) +{ + int firstStyleId = plt->getFirstUnpagedStyle(); + if (firstStyleId == -1) + firstStyleId = plt->getStyleCount(); + usedInks[index] = firstStyleId; + plt->getPage(0)->addStyle(TPixel32::Red); +} + +//------------------------------------------------------------ +//std::map& usedInk upInkId -> downInkId +void doMergeCmapped(TRasterCM32P rasOut, const TRasterCM32P &rasUp, const TPaletteP &pltOut, bool onlyInks, + int matchlinePrevalence, std::map &usedInks) +{ + double val = matchlinePrevalence / 100.0; //matchlinePrevalence ==0 always ink down; matchlinePrevalence == 100 always ink up; + assert(rasOut && rasUp); + assert(rasOut->getSize() == rasUp->getSize()); + + TPaletteP downPlt = pltOut->clone(); + std::map::iterator it; + for (it = usedInks.begin(); it != usedInks.end(); it++) + downPlt->getPage(0)->addStyle(TPixel32::Red); + for (int y = 0; y < rasOut->getLy(); y++) { + TPixelCM32 *pixDown = rasOut->pixels(y), *endPix = pixDown + rasOut->getLx(); + TPixelCM32 *pixUp = rasUp->pixels(y); + + while (pixDown < endPix) { + //there is lines in matchline + if (!pixUp->isPurePaint()) { + int toneDown = pixDown->getTone(); + int toneUp = pixUp->getTone(); + if (usedInks.find(pixUp->getInk()) == usedInks.end()) + addColor(downPlt, pixUp->getInk(), usedInks); + + if (val == 1) { //Matchline is on top, with gap + pixDown->setTone(toneUp); + pixDown->setInk(usedInks[pixUp->getInk()]); + } else if (val == 0) { //Matchline is on bottom, with gap + if (pixDown->isPurePaint()) { + pixDown->setTone(toneUp); + pixDown->setInk(usedInks[pixUp->getInk()]); + } else { + //*pixOut = *pixDown; + } + } else { + if ((val > 0 && toneUp < toneDown) || (val == 0 && toneDown == 255)) //(toneUpsetTone(toneUp); + + if ((255 - toneDown) * (1 - val) <= val * (255 - toneUp - 1)) //val==0 -> if (toneDown== 255).... + //val==0.5 -> if (toneup if (toneup<255)... + pixDown->setInk(usedInks[pixUp->getInk()]); + } + } + int paintIndex; + if (!onlyInks && (paintIndex = pixUp->getPaint()) > 0) { + if (usedInks.find(paintIndex) == usedInks.end()) + addColor(downPlt, paintIndex, usedInks); + pixDown->setPaint(usedInks[paintIndex]); + } + pixUp++; + pixDown++; + //pixOut++; + } + } +} +/*------------------------------------------------------------------------*/ + +void addColors(const TPixelCM32 &color, TPaletteP plt, const TPaletteP &upPlt, std::map &usedColors) +{ + if (usedColors.find(color.getInk()) == usedColors.end()) + addColor(plt, color.getInk(), upPlt, usedColors); + if (usedColors.find(color.getPaint()) == usedColors.end()) + addColor(plt, color.getPaint(), upPlt, usedColors); +} + +//--------------------------------------------------------------------------- +void addColors(const TPixelCM32 &color, TPaletteP plt, std::map &usedColors) +{ + if (usedColors.find(color.getInk()) == usedColors.end()) + addColor(plt, color.getInk(), usedColors); + if (usedColors.find(color.getPaint()) == usedColors.end()) + addColor(plt, color.getPaint(), usedColors); +} + +/*------------------------------------------------------------------------*/ + +void doApplyMatchLines(TRasterCM32P rasOut, const TRasterCM32P &rasUp, int inkIndex, int matchlinePrevalence) +{ + double val = matchlinePrevalence / 100.0; //matchlinePrevalence ==0->always ink down; matchlinePrevalence == 100 always ink up; + + assert(rasOut && rasUp); + assert(rasOut->getSize() == rasUp->getSize()); + + for (int y = 0; y < rasOut->getLy(); y++) { + TPixelCM32 *pixDown = rasOut->pixels(y), *endPix = pixDown + rasOut->getLx(); + TPixelCM32 *pixUp = rasUp->pixels(y); + + while (pixDown < endPix) { + if (!pixUp->isPurePaint()) { + int toneDown = pixDown->getTone(); + int toneUp = pixUp->getTone(); + if (val == 1) { //Matchline is on top, with gap + pixDown->setTone(toneUp); + pixDown->setInk(inkIndex); + } else if (val == 0) { //Matchline is on bottom, with gap + if (pixDown->isPurePaint()) { + pixDown->setTone(toneUp); + pixDown->setInk(inkIndex); + } else { + } + //*pixOut = *pixDown; + } + if ((val > 0 && toneUp < toneDown) || (val == 0 && toneDown == 255)) + pixDown->setTone(toneUp); + + if ((255 - toneDown) * (1 - val) <= val * (255 - toneUp - 1)) //val==0 -> if (toneDown == 255).... + //val==0.5 -> if (toneup if (toneup<255)... + pixDown->setInk(inkIndex); + } + + pixUp++; + pixDown++; + } + } +} +/*------------------------------------------------------------------------*/ + +} //namespace + +#ifdef LEVO + +void TRop::eraseColors(TRasterCM32P ras, vector &colorIds, bool eraseInks, bool keepColor) +{ + assert(ras); + + vector curColorIds; + std::sort(colorIds.begin(), colorIds.end()); + + if (!keepColor) + curColorIds = colorIds; + else { + //prendo tutti i colori ECCETTO quelli nel vettore colorIds + unsigned int count1 = 0, count2 = 0; + int size = eraseInks ? TPixelCM32::getMaxInk() : TPixelCM32::getMaxPaint(); + + curColorIds.resize(size + 1 - colorIds.size()); + for (int i = 0; i < size; i++) + if (count1 < colorIds.size() && colorIds[count1] == i) + count1++; + else + curColorIds[count2++] = i; + } + + for (int y = 0; y < ras->getLy(); y++) { + TPixelCM32 *pix = ras->pixels(y), *endPix = pix + ras->getLx(); + + while (pix < endPix) { + unsigned int i; + int color = eraseInks ? pix->getInk() : pix->getPaint(); + + if (color != 0) + for (i = 0; i < curColorIds.size() && curColorIds[i] <= color; i++) + if (color == curColorIds[i]) { + *pix = eraseInks ? TPixelCM32(0, pix->getPaint(), TPixelCM32::getMaxTone()) : TPixelCM32(pix->getInk(), 0, pix->getTone()); + break; + } + pix++; + } + } +} + +#endif + +/*------------------------------------------------------------------------*/ + +void TRop::eraseColors(TRasterCM32P ras, vector *colorIds, bool eraseInks) +{ + if (colorIds) + std::sort(colorIds->begin(), colorIds->end()); + + for (int y = 0; y < ras->getLy(); y++) { + TPixelCM32 *pix = ras->pixels(y), *endPix = pix + ras->getLx(); + + for (; pix < endPix; pix++) { + unsigned int i = 0; + int color = eraseInks ? pix->getInk() : pix->getPaint(); + + if (color == 0) + continue; + + if (colorIds) { + while (i < colorIds->size() && (*colorIds)[i] < color) + i++; + if (i == colorIds->size() || color != (*colorIds)[i]) + continue; + } + + if (eraseInks) { + pix->setInk(0); + pix->setTone(TPixelCM32::getMaxTone()); + } else + pix->setPaint(0); + } + } +} + +//-------------------------------- +/* +void TRop::overaCmapped(TRasterCM32P rasOut, const TRasterCM32P& rasUp, const TPaletteP &pltOut, int matchlinePrevalence, std::map& usedColors) +{ +doMergeCmapped(rasOut, rasUp, pltOut, false, matchlinePrevalence, usedColors); +} +*/ +//----------------------------------------------------------------- + +void TRop::applyMatchLines(TRasterCM32P rasOut, const TRasterCM32P &rasUp, const TPaletteP &pltOut, int inkIndex, int matchlinePrevalence, std::map &usedInks) +{ + assert(matchlinePrevalence >= 0); + if (inkIndex == -1) + doMergeCmapped(rasOut, rasUp, pltOut, true, matchlinePrevalence, usedInks); + else + doApplyMatchLines(rasOut, rasUp, inkIndex, matchlinePrevalence); +} + +/*------------------------------------------------------------------------*/ + +void TRop::eraseStyleIds(TToonzImage *image, const vector styleIds) +{ + assert(image); + TRasterCM32P ras = image->getRaster(); + + int i; + for (i = 0; i < (int)styleIds.size(); i++) { + int styleId = styleIds[i]; + ras->lock(); + for (int y = 0; y < ras->getLy(); y++) { + TPixelCM32 *pix = ras->pixels(y), *endPix = pix + ras->getLx(); + while (pix < endPix) { + bool isPaint = (pix->getPaint() == styleId); + bool isInk = (pix->getInk() == styleId); + if (!isPaint && !isInk) { + pix++; + continue; + } else if (isPaint && !isInk) + *pix = TPixelCM32(pix->getInk(), 0, pix->getTone()); + else if (!isPaint && isInk) + *pix = TPixelCM32(0, pix->getPaint(), TPixelCM32::getMaxTone()); + else if (isPaint && isInk) + *pix = TPixelCM32(0, 0, pix->getTone()); + else + assert(0); + pix++; + } + } + ras->unlock(); + } +} + +/*------------------------------------------------------------------------*/ + +inline bool isTransparent(TPixelCM32 *pix) +{ + return (((*((ULONG *)pix)) & 0x000fffff) == 0xff); +} + +void TRop::overlayCmapped(TRasterCM32P rasOut, const TRasterCM32P &rasUp, const TPaletteP &pltOut, const TPaletteP &upPlt, std::map &usedColors) +{ + + assert(rasOut && rasUp); + assert(rasOut->getSize() == rasUp->getSize()); + + TPaletteP downPlt = pltOut->clone(); + std::map::iterator it; + for (it = usedColors.begin(); it != usedColors.end(); it++) + downPlt->getPage(0)->addStyle(TPixel32::Red); + + for (int y = 0; y < rasOut->getLy(); y++) { + TPixelCM32 *pixDown = rasOut->pixels(y); + TPixelCM32 *pixUp = rasUp->pixels(y); + + for (int x = 0; x < rasOut->getLx(); x++, pixUp++, pixDown++) { + if (isTransparent(pixUp)) //WARNING! cannot check transparent pixels with *pixup==TPixelCM32() since also 0x183000ff i.e., is a valid transparent value + continue; + int tone = pixUp->getTone(); + if (isTransparent(pixDown) || tone == 255 || tone == 0 || pixUp->getPaint() != 0) //up e' punto interno, o esterno non antialiasato + { + addColors(*pixUp, downPlt, upPlt, usedColors); + pixDown->setInk(usedColors[pixUp->getInk()]); + pixDown->setPaint(usedColors[pixUp->getPaint()]); + pixDown->setTone(tone); + } else if (tone <= pixDown->getTone() || tone < 128) //up e' bordo esterno antialiasato piu' opaco di down + { + int ink = pixUp->getInk(); + if (usedColors.find(ink) == usedColors.end()) + addColor(downPlt, ink, upPlt, usedColors); + pixDown->setInk(usedColors[ink]); + pixDown->setTone(tone < 128 ? 0 : tone); + } + } + } +} + +//----------------------------------------------------- + +namespace +{ + +#define MAGICFAC (257U * 256U + 1U) +/* +#define PIX_CM32_PENMAP_IDX(PIX) ((PIX)>>12 & 0xfff00 | (PIX) & 0xff) +#define PIX_CM32_COLMAP_IDX(PIX) ((PIX) & 0xfffff) + + +#define PIX_CM32_PENMAP_COLMAP_TO_RGBM(PIX,PENMAP,COLMAP,RES) \ + { \ + ULONG _r = (PIX); \ + ULONG _s = ((ULONG *)(PENMAP))[MY_PIX_CM32_PENMAP_IDX(_r)] + \ + ((ULONG *)(COLMAP))[MY_PIX_CM32_COLMAP_IDX(_r)]; \ + (RES) = *(LPIXEL *)(&_s); \ + } + */ + +/* +void fillCmapRamp (RAS_CMAP& cmap, const TPixel32& color, int index) +{ +index = index << cmap.info.tone_bits; +int xedni = index + cmap.info.n_tones-1; + +TPixel32 _color=color; + UINT fac = MAGICFAC * _color.m; + _color.r=(UINT)(_color.r * fac + (1U<<23))>>24; + _color.b=(UINT)(_color.b * fac + (1U<<23))>>24; + _color.g=(UINT)(_color.g * fac + (1U<<23))>>24; + +for (int tone = 0; tone < cmap.info.n_tones; tone++) + { + LPIXEL val; + UINT magic_tone = tone * MAGICFAC; + val.r = (UCHAR)((_color.r * magic_tone + (1<<23)) >> 24); + val.g = (UCHAR)((_color.g * magic_tone + (1<<23)) >> 24); + val.b = (UCHAR)((_color.b * magic_tone + (1<<23)) >> 24); + val.m = (UCHAR)((_color.m * magic_tone + (1<<23)) >> 24); + + cmap.colbuffer[index++] = val; + cmap.penbuffer[xedni--] = val; + } +} +*/ + +/*---------------------------------------------------------------------------*/ + +} //namespace + +/*---------------------------------------------------------------------------*/ + +// \b NOTE: Starting from Toonz 6.1, some important improvements are introduced: +// a) The passed raster is now referenced by the returned _RASTER*, just the same way +// smartpointer to rasters do. +// b) The cache is made aware of the passed raster, mainly because these old 4.6 raster +// structures are essentially used for memory-consuming operations with tlv fxs and may +// need to be shipped to hard disk on critical situations (a matter handled by the cache). +// c) The lockRaster and unlockRaster functions are provided. They are meant to specify whether +// the raster is actively referenced by the raster pointer, or is rather in a lazy state - +// so that the cache may move it to hard disk if necessary. + +/* +static const TCM_INFO Tcm_my_default_info = { 8, 8, 12, 20, 12, + 0x0000, 0x00ff, + 256, 4096, 256 + };*/ + +/*---------------------------------------------------------------------------*/ + +inline LPIXEL premultiplyLPIXEL(const TPixel32 &pix) +{ + //int MAGICFAC = (257U * 256U + 1U); + UINT fac = MAGICFAC * pix.m; + LPIXEL out; + out.r = ((UINT)(pix.r * fac + (1U << 23)) >> 24); + out.g = ((UINT)(pix.g * fac + (1U << 23)) >> 24); + out.b = ((UINT)(pix.b * fac + (1U << 23)) >> 24); + out.m = pix.m; + return out; +} + +_RASTER *TRop::convertRaster50to46(const TRasterP &inRas, const TPaletteP &inPalette) +{ + + int lx = inRas->getLx(); + int ly = inRas->getLy(); + int wrap = inRas->getWrap(); + + assert(lx > 0 && ly > 0); + assert(inRas->getRawData()); + TRasterGR8P rgr8 = (TRasterGR8P)inRas; + TRasterGR16P rgr16 = (TRasterGR16P)inRas; + TRaster32P r32 = (TRaster32P)inRas; + TRaster64P r64 = (TRaster64P)inRas; + TRasterCM32P rcm = (TRasterCM32P)inRas; + + RASTER *rout = new RASTER; + memset(rout, 0, sizeof(RASTER)); + + std::string id(TImageCache::instance()->getUniqueId()); + rout->cacheIdLength = id.size(); + rout->cacheId = new char[rout->cacheIdLength]; + memcpy(rout->cacheId, id.data(), sizeof(char) * rout->cacheIdLength); + { + TImageP img; + if (rcm) + img = TToonzImageP(rcm, rcm->getBounds()); //saveBox is not considered in RASTER struct anyway + else + img = TRasterImageP(inRas); + TImageCache::instance()->add(id, img); + } + inRas->addRef(); + + rout->buffer = inRas->getRawData(); + TRasterP parent = inRas->getParent(); + rout->native_buffer = (parent) ? parent->getRawData() : inRas->getRawData(); + rout->lx = lx; + rout->ly = ly; + rout->wrap = wrap; + + if (rgr8) + rout->type = RAS_GR8; + else if (rgr16) + rout->type = RAS_GR16; + else if (r32) + rout->type = RAS_RGBM; + else if (r64) + rout->type = RAS_RGBM64; + else if (rcm) + rout->type = RAS_CM32; + else + assert(!"raster type not convertible!"); + + if (rout->type != RAS_CM32) + return rout; + + if (!inPalette) { + assert(!"missing palette!"); + return NULL; + } + + rout->cmap.info = Tcm_32_default_info; + rout->cmap.buffer = new LPIXEL[TCM_CMAP_PENBUFFER_SIZE(rout->cmap.info)]; + //rout->cmap.penbuffer = new LPIXEL[TCM_CMAP_PENBUFFER_SIZE (rout->cmap.info)]; + //rout->cmap.colbuffer = new LPIXEL[TCM_CMAP_COLBUFFER_SIZE (rout->cmap.info)]; + + for (int i = 0; i < inPalette->getStyleCount(); i++) + rout->cmap.buffer[i] = ::premultiplyLPIXEL(inPalette->getStyle(i)->getAverageColor()); + //fillCmapRamp (rout->cmap, inPalette->getStyle(i)->getMainColor(), i); + + return rout; +} + +/*------------------------------------------------------------------------*/ + +void TRop::releaseRaster46(_RASTER *&r, bool doReleaseBuffer) +{ + //Buffer release no more supported. These are now intended as smart + //pointers to rasters - they release themselves on their own. + assert(!doReleaseBuffer); + + if (r->type == RAS_CM32) { + delete[] r->cmap.buffer; + //delete [] r->cmap.penbuffer; + //delete [] r->cmap.colbuffer; + } + + if (doReleaseBuffer && r->native_buffer == r->buffer) + delete r->buffer; //Should not happen + + //Unlock if locked, and remove the cache reference + if (r->buffer) + unlockRaster(r); + TImageCache::instance()->remove(std::string(r->cacheId, r->cacheIdLength)); + delete[] r->cacheId; + + delete r; + r = 0; +} + +/*------------------------------------------------------------------------*/ + +void TRop::lockRaster(RASTER *raster) +{ + TImageP img(TImageCache::instance()->get(std::string(raster->cacheId, raster->cacheIdLength), true)); + TRasterP cacheRas; + if (raster->type == RAS_CM32) + cacheRas = TToonzImageP(img)->getRaster(); + else + cacheRas = TRasterImageP(img)->getRaster(); + cacheRas->addRef(); + raster->buffer = cacheRas->getRawData(); + TRasterP parent = cacheRas->getParent(); + raster->native_buffer = (parent) ? parent->getRawData() : cacheRas->getRawData(); +} + +/*------------------------------------------------------------------------*/ + +void TRop::unlockRaster(RASTER *raster) +{ + TImageP img(TImageCache::instance()->get(std::string(raster->cacheId, raster->cacheIdLength), true)); + TRasterP cacheRas; + if (raster->type == RAS_CM32) + cacheRas = TToonzImageP(img)->getRaster(); + else + cacheRas = TRasterImageP(img)->getRaster(); + cacheRas->release(); + raster->buffer = 0; + raster->native_buffer = 0; +} + +/*------------------------------------------------------------------------*/ + +_RASTER *TRop::readRaster46(const char *filename) +{ + //No more called in Toonz... + + TImageP img; + + TImageReader::load(TFilePath(filename), img); + + if ((TToonzImageP)img) + return 0; + + TRasterImageP rimg = (TRasterImageP)img; + + if (!rimg) + return 0; + TRasterP ras = rimg->getRaster(); + + return TRop::convertRaster50to46(ras, 0); +} + +namespace +{ + +inline TPixel32 getPix32(const TPixelCM32 &pixcm, + const std::vector &colors) +{ + int t = pixcm.getTone(); + int p = pixcm.getPaint(); + int i = pixcm.getInk(); + + if (t == TPixelCM32::getMaxTone()) + return colors[p]; + else if (t == 0) + return colors[i]; + else + return blend(colors[i], colors[p], t, TPixelCM32::getMaxTone()); +} + +} //namespace + +//from 4.6 + +#ifdef LEVO + +void TRop::zoomOutCm32Rgbm(const TRasterCM32P &rin, TRaster32P &rout, const TPalette &plt, + int x1, int y1, int x2, int y2, + int newx, int newy, int absZoomLevel) +{ + TPixelCM32 *rowin, *pixin, *in, win; + TPixel32 *rowout, *pixout, valin, valout; + int tmp_r, tmp_g, tmp_b, tmp_m; + int wrapin, wrapout; + int x, y, lx, ly, xlast, ylast, xrest, yrest, i, j; + int factor, fac_fac_2_bits; + int fac_fac, yrest_fac, fac_xrest, yrest_xrest; + int fac_fac_2, yrest_fac_2, fac_xrest_2, yrest_xrest_2; + int fac_fac_4; + + int count = plt.getStyleCount(); + int count2 = tmax(count, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + std::vector colors(count2); + for (i = 0; i < plt.getStyleCount(); i++) + colors[i] = ::premultiply(plt.getStyle(i)->getAverageColor()); + + lx = x2 - x1 + 1; + ly = y2 - y1 + 1; + factor = 1 << absZoomLevel; + xrest = lx & (factor - 1); + yrest = ly & (factor - 1); + xlast = x2 - xrest + 1; + ylast = y2 - yrest + 1; + fac_fac = factor * factor; + fac_fac_2 = fac_fac >> 1; + fac_fac_4 = fac_fac >> 2; + fac_fac_2_bits = 2 * absZoomLevel - 1; + yrest_fac = yrest * factor; + yrest_fac_2 = yrest_fac >> 1; + fac_xrest = factor * xrest; + fac_xrest_2 = fac_xrest >> 1; + yrest_xrest = yrest * xrest; + yrest_xrest_2 = yrest_xrest >> 1; + wrapin = rin->getWrap(); + wrapout = rout->getWrap(); + valout.m = 0xff; + + rowin = (TPixelCM32 *)rin->getRawData() + wrapin * y1 + x1; + rowout = (TPixel32 *)rout->getRawData() + wrapout * newy + newx; + for (y = y1; y < ylast; y += factor) { + pixin = rowin; + pixout = rowout; + for (x = x1; x < xlast; x += factor) { + tmp_r = tmp_g = tmp_b = tmp_m = 0; + in = pixin; + for (j = 0; j < factor; j += 2) { + for (i = 0; i < factor; i += 2) { + win = *in; + valin = getPix32(win, colors); + tmp_r += valin.r; + tmp_g += valin.g; + tmp_b += valin.b; + tmp_m += valin.m; + in += wrapin; + in++; + win = *in; + valin = getPix32(win, colors); + tmp_r += valin.r; + tmp_g += valin.g; + tmp_b += valin.b; + tmp_m += valin.m; + in -= wrapin; + in++; + } + in += 2 * wrapin - factor; + } + valout.r = (tmp_r + fac_fac_4) >> fac_fac_2_bits; + valout.g = (tmp_g + fac_fac_4) >> fac_fac_2_bits; + valout.b = (tmp_b + fac_fac_4) >> fac_fac_2_bits; + valout.m = (tmp_m + fac_fac_4) >> fac_fac_2_bits; + *pixout++ = valout; + pixin += factor; + } + if (xrest) { + tmp_r = tmp_g = tmp_b = tmp_m = 0; + for (j = 0; j < factor; j++) + for (i = 0; i < xrest; i++) { + win = pixin[i + j * wrapin]; + valin = getPix32(win, colors); + tmp_r += valin.r; + tmp_g += valin.g; + tmp_b += valin.b; + tmp_m += valin.m; + } + valout.r = (tmp_r + fac_xrest_2) / fac_xrest; + valout.g = (tmp_g + fac_xrest_2) / fac_xrest; + valout.b = (tmp_b + fac_xrest_2) / fac_xrest; + valout.m = (tmp_m + fac_xrest_2) / fac_xrest; + *pixout = valout; + } + rowin += wrapin * factor; + rowout += wrapout; + } + if (yrest) { + pixin = rowin; + pixout = rowout; + for (x = x1; x < xlast; x += factor) { + tmp_r = tmp_g = tmp_b = tmp_m = 0; + for (j = 0; j < yrest; j++) + for (i = 0; i < factor; i++) { + win = pixin[i + j * wrapin]; + valin = getPix32(win, colors); + tmp_r += valin.r; + tmp_g += valin.g; + tmp_b += valin.b; + tmp_m += valin.m; + } + valout.r = (tmp_r + yrest_fac_2) / yrest_fac; + valout.g = (tmp_g + yrest_fac_2) / yrest_fac; + valout.b = (tmp_b + yrest_fac_2) / yrest_fac; + valout.m = (tmp_m + yrest_fac_2) / yrest_fac; + *pixout++ = valout; + pixin += factor; + } + if (xrest) { + tmp_r = tmp_g = tmp_b = tmp_m = 0; + for (j = 0; j < yrest; j++) + for (i = 0; i < xrest; i++) { + win = pixin[i + j * wrapin]; + valin = getPix32(win, colors); + tmp_r += valin.r; + tmp_g += valin.g; + tmp_b += valin.b; + tmp_m += valin.m; + } + valout.r = (tmp_r + yrest_xrest_2) / yrest_xrest; + valout.g = (tmp_g + yrest_xrest_2) / yrest_xrest; + valout.b = (tmp_b + yrest_xrest_2) / yrest_xrest; + valout.m = (tmp_m + yrest_xrest_2) / yrest_xrest; + *pixout = valout; + } + } +} + +#endif + +/*-----------------------------------------------------------------------------*/ + +#ifdef LEVO + +void TRop::makeIcon(TRaster32P &_rout, const TRasterCM32P &rin, const TPaletteP &palette, bool onBlackBg) +{ + int i, j; + int lx = rin->getLx(); + int ly = rin->getLy(); + + int count = palette->getStyleCount(); + int count2 = tmax(count, TPixelCM32::getMaxInk(), TPixelCM32::getMaxPaint()); + std::vector colors(count2); + for (i = 0; i < palette->getStyleCount(); i++) + colors[i] = /*::premultiply(*/ palette->getStyle(i)->getAverageColor(); //); + + TDimension dim(_rout->getSize()); + TRaster32P rout; + + double arIn = (double)lx / ly; + double arOut = (double)dim.lx / dim.ly; + if (!areAlmostEqual(arIn, arOut, 1e-2)) //do not want a stretched icon! I extract a subraster with same aspect ration of rin + { + int newlx, newly; + if (arOut < arIn) { + newlx = dim.lx; + newly = dim.lx / arIn; + } else { + newly = dim.ly; + newlx = dim.ly * arIn; + } + _rout->clear(); + rout = _rout->extract((dim.lx - newlx) / 2, (dim.ly - newly) / 2, + (dim.lx - newlx) / 2 + newlx - 1, (dim.ly - newly) / 2 + newly - 1); + dim = rout->getSize(); + } else + rout = _rout; + + TPixel32 *pixOut0 = (TPixel32 *)rout->getRawData(); + + int countY = 0; + bool newRow = true; + int currTone = TPixelCM32::getMaxTone(); + + for (i = 0; i < ly; i++) { + TPixelCM32 *pixIn = (TPixelCM32 *)rin->pixels(i); + TPixel32 *pixOut = pixOut0; + int countX = 0; + for (j = 0; j < lx; j++) { + if (newRow || currTone > pixIn->getTone()) { + currTone = pixIn->getTone(); + if (onBlackBg) + *pixOut = overPixOnBlack(getPix32(*pixIn, colors)); + else + *pixOut = overPixOnWhite(getPix32(*pixIn, colors)); + } + pixIn++; + countX += dim.lx; + if (countX >= lx) + countX -= lx, pixOut++, currTone = TPixelCM32::getMaxTone(); + } + + countY += dim.ly; + if (countY >= ly) { + countY -= ly; + pixOut0 += rout->getWrap(); + currTone = TPixelCM32::getMaxTone(); + newRow = true; + } else + newRow = false; + } +} + +#endif + +/*-----------------------------------------------------------------------------*/ + +void TRop::makeIcon(TRasterCM32P &_rout, const TRasterCM32P &rin) +{ + int i, j; + int lx = rin->getLx(); + int ly = rin->getLy(); + + TDimension dim(_rout->getSize()); + TRasterCM32P rout; + + rout = _rout; + + rout->lock(); + rin->lock(); + TPixelCM32 *pixOut0 = (TPixelCM32 *)rout->getRawData(); + + int countY = 0; + bool newRow = true; + + for (i = 0; i < ly; i++) { + TPixelCM32 *pixIn = (TPixelCM32 *)rin->pixels(i); + TPixelCM32 *pixOut = pixOut0; + int countX = 0; + bool newColumn = true; + for (j = 0; j < lx; j++) { + if ((newRow && newColumn) || + pixOut->getTone() > pixIn->getTone()) + *pixOut = *pixIn; + + pixIn++; + countX += dim.lx; + if (countX >= lx) { + countX -= lx, pixOut++; + newColumn = true; + } else + newColumn = false; + } + + countY += dim.ly; + if (countY >= ly) { + countY -= ly; + pixOut0 += rout->getWrap(); + newRow = true; + } else + newRow = false; + } + rout->unlock(); + rin->unlock(); +} + +/*-----------------------------------------------------------------------------*/ diff --git a/toonz/sources/common/tsound/tsop.cpp b/toonz/sources/common/tsound/tsop.cpp new file mode 100644 index 0000000..b3207a2 --- /dev/null +++ b/toonz/sources/common/tsound/tsop.cpp @@ -0,0 +1,2021 @@ + + +#include "tsop.h" +#include "tsound_t.h" + +//TRop::ResampleFilterType Tau_resample_filter = Hamming3; + +//--------------------------------------------------------- + +typedef enum { + FLT_NONE, + + FLT_TRIANGLE, /* triangle filter */ + FLT_MITCHELL, /* Mitchell-Netravali filter */ + FLT_CUBIC_5, /* cubic convolution, a = .5 */ + FLT_CUBIC_75, /* cubic convolution, a = .75 */ + FLT_CUBIC_1, /* cubic convolution, a = 1 */ + FLT_HANN2, /* Hann window, rad = 2 */ + FLT_HANN3, /* Hann window, rad = 3 */ + FLT_HAMMING2, /* Hamming window, rad = 2 */ + FLT_HAMMING3, /* Hamming window, rad = 3 */ + FLT_LANCZOS2, /* Lanczos window, rad = 2 */ + FLT_LANCZOS3, /* Lanczos window, rad = 3 */ + FLT_GAUSS, /* Gaussian convolution */ + + FLT_HOW_MANY +} FLT_TYPE; + +//--------------------------------------------------------- + +typedef enum { + RESORDER_NONE, + RESORDER_BITS_RATE_CHANS, + RESORDER_CHANS_RATE_BITS, + RESORDER_RATE_BITS_CHANS, + RESORDER_CHANS_BITS_RATE, + RESORDER_BITS_RATE, + RESORDER_RATE_BITS, + RESORDER_CHANS_RATE, + RESORDER_RATE_CHANS, + RESORDER_RATE, + RESORDER_SIGN, + RESORDER_HOW_MANY +} RESORDER_TYPE; + +//--------------------------------------------------------- + +typedef struct + { + int src_offset; /* of weight[0] relative to start of period */ + int n_weights; + double *weight; /* [n_weights], -1.0 <= weight <= 1.0 */ +} WEIGHTSET; + +//--------------------------------------------------------- + +typedef struct + { + int src_period; /* after a period src and dst are in step again */ + int dst_period; + WEIGHTSET *weightset; /* [dst_period] */ +} FILTER; + +//--------------------------------------------------------- + +#define M_PIF ((float)TConsts::pi) +#define SINC0(x, a) (sin((TConsts::pi / (a)) * (x)) / ((TConsts::pi / (a)) * (x))) +#define SINC0F(x, a) (sinf((M_PIF / (a)) * (x)) / ((M_PIF / (a)) * (x))) +#define SINC(x, a) ((x) == 0.0 ? 1.0 : SINC0(x, a)) +#define SINCF(x, a) ((x) == 0.0F ? 1.0F : SINC0F(x, a)) + +#define FULL_INT_MUL_DIV(X, Y, Z) ((int)(((double)(X) * (Y) + ((Z)-1)) / (Z))) + +//prototipi +void convert(TSoundTrackP &dst, const TSoundTrackP &src); + +namespace +{ + +//--------------------------------------------------------- + +void simplifyRatio(int *p_a, int *p_b) +{ + int a = *p_a, b = *p_b; + + while (a != b) + if (a > b) + a -= b; + else + b -= a; + if (a != 1) { + *p_a /= a; + *p_b /= a; + } +} + +//--------------------------------------------------------- + +int getFilterRadius(FLT_TYPE flt_type) +{ + int result = 0; + switch (flt_type) { + case FLT_TRIANGLE: + result = 1; + CASE FLT_MITCHELL : result = 2; + CASE FLT_CUBIC_5 : result = 2; + CASE FLT_CUBIC_75 : result = 2; + CASE FLT_CUBIC_1 : result = 2; + CASE FLT_HANN2 : result = 2; + CASE FLT_HANN3 : result = 3; + CASE FLT_HAMMING2 : result = 2; + CASE FLT_HAMMING3 : result = 3; + CASE FLT_LANCZOS2 : result = 2; + CASE FLT_LANCZOS3 : result = 3; + CASE FLT_GAUSS : result = 2; + DEFAULT: + assert(!"bad filter type"); + break; + } + return result; +} + +//--------------------------------------------------------- + +double filterValue(FLT_TYPE flt_type, double x) +{ + if (!x) + return 1.0; + double result; + switch (flt_type) { + case FLT_TRIANGLE: + if (x < -1.0) + result = 0.0; + else if (x < 0.0) + result = 1.0 + x; + else if (x < 1.0) + result = 1.0 - x; + else + result = 0.0; + + CASE FLT_MITCHELL: + { + static double p0, p2, p3, q0, q1, q2, q3; + + if (!p0) { + const double b = 1.0 / 3.0; + const double c = 1.0 / 3.0; + + p0 = (6.0 - 2.0 * b) / 6.0; + p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0; + p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0; + q0 = (8.0 * b + 24.0 * c) / 6.0; + q1 = (-12.0 * b - 48.0 * c) / 6.0; + q2 = (6.0 * b + 30.0 * c) / 6.0; + q3 = (-b - 6.0 * c) / 6.0; + } + if (x < -2.0) + result = 0.0; + else if (x < -1.0) + result = (q0 - x * (q1 - x * (q2 - x * q3))); + else if (x < 0.0) + result = (p0 + x * x * (p2 - x * p3)); + else if (x < 1.0) + result = (p0 + x * x * (p2 + x * p3)); + else if (x < 2.0) + result = (q0 + x * (q1 + x * (q2 + x * q3))); + } + + CASE FLT_CUBIC_5 : if (x < 0.0) x = -x; + else if (x < 1.0) result = 2.5 * x * x * x - 3.5 * x * x + 1; + else if (x < 2.0) result = 0.5 * x * x * x - 2.5 * x * x + 4 * x - 2; + + CASE FLT_CUBIC_75 : if (x < 0.0) x = -x; + else if (x < 1.0) result = 2.75 * x * x * x - 3.75 * x * x + 1; + else if (x < 2.0) result = 0.75 * x * x * x - 3.75 * x * x + 6 * x - 3; + + CASE FLT_CUBIC_1 : if (x < 0.0) x = -x; + else if (x < 1.0) result = 3 * x * x * x - 4 * x * x + 1; + else if (x < 2.0) result = x * x * x - 5 * x * x + 8 * x - 4; + + CASE FLT_HANN2 : if (x <= -2.0) result = 0.0; + else if (x < 2.0) result = SINC0(x, 1) * (0.5 + 0.5 * cos((TConsts::pi_2)*x)); + + CASE FLT_HANN3 : if (x <= -3.0) result = 0.0; + else if (x < 3.0) result = SINC0(x, 1) * (0.5 + 0.5 * cos((TConsts::pi / 3) * x)); + + CASE FLT_HAMMING2 : if (x <= -2.0) result = 0.0; + else if (x < 2.0) result = SINC0(x, 1) * (0.54 + 0.46 * cos((TConsts::pi_2)*x)); + + CASE FLT_HAMMING3 : if (x <= -3.0) result = 0.0; + else if (x < 3.0) result = SINC0(x, 1) * (0.54 + 0.46 * cos((TConsts::pi / 3) * x)); + + CASE FLT_LANCZOS2 : if (x <= -2.0) result = 0.0; + else if (x < 2.0) result = SINC0(x, 1) * SINC0(x, 2); + + CASE FLT_LANCZOS3 : if (x <= -3.0) result = 0.0; + else if (x < 3.0) result = SINC0(x, 1) * SINC0(x, 3); + + CASE FLT_GAUSS : if (x <= -2.0) result = 0.0; + else if (x < 2.0) result = exp((-TConsts::pi) * x * x); + /* exp(-M_PI*2*2)~=3.5*10^-6 */ + + DEFAULT: + assert(!"bad filter type"); + } + return result; +} + +//--------------------------------------------------------- + +} //namespace + +//============================================================================== + +template +void convertSamplesT(TSoundTrackT &dst, const TSoundTrackT &src) +{ + const T2 *srcSample = src.samples(); + T1 *dstSample = dst.samples(); + + const T2 *srcEndSample = srcSample + tmin(src.getSampleCount(), dst.getSampleCount()); + while (srcSample < srcEndSample) { + *dstSample = T1::from(*srcSample); + ++dstSample; + ++srcSample; + } +} + +//============================================================================== + +template +T *resampleT(T &src, TINT32 sampleRate, FLT_TYPE flt_type) +{ + typedef typename T::SampleType SampleType; + typedef typename T::SampleType::ChannelValueType ChannelValueType; + T *dst = new TSoundTrackT( + sampleRate, + src.getChannelCount(), + (TINT32)(src.getSampleCount() * (sampleRate / (double)src.getSampleRate()))); + + double src_rad, f, src_f0, src_to_f; + double weight, weightsum; + int iw, is, s0, ip, first, last; + + FILTER filter; + filter.src_period = (int)src.getSampleRate(); + filter.dst_period = (int)dst->getSampleRate(); + simplifyRatio(&filter.src_period, &filter.dst_period); + filter.weightset = new WEIGHTSET[filter.dst_period]; + if (!filter.weightset) + return 0; + + int flt_rad = getFilterRadius(flt_type); + double dstRate = (double)dst->getSampleRate(); + double srcRate = (double)src.getSampleRate(); + double dst_to_src = srcRate / dstRate; + if (srcRate > dstRate) { + src_rad = flt_rad * dst_to_src; + src_to_f = dstRate / srcRate; + } else { + src_rad = flt_rad; + src_to_f = 1.0; + } + + for (ip = 0; ip < filter.dst_period; ip++) { + src_f0 = ip * dst_to_src; + if (ip == 0 && srcRate < dstRate) + first = last = 0; + else { + first = intGT(src_f0 - src_rad); + last = intLT(src_f0 + src_rad); + } + + filter.weightset[ip].src_offset = first; + filter.weightset[ip].n_weights = last - first + 1; + filter.weightset[ip].weight = new double[filter.weightset[ip].n_weights]; + + if (!filter.weightset[ip].weight) + return 0; + + weightsum = 0.0; + for (is = first; is <= last; is++) { + f = (is - src_f0) * src_to_f; + weight = filterValue(flt_type, f); + filter.weightset[ip].weight[is - first] = weight; + weightsum += weight; + } + assert(weightsum); + for (is = first; is <= last; is++) + filter.weightset[ip].weight[is - first] /= weightsum; + } + + ip = 0; + s0 = 0; + + for (TINT32 id = 0; id < dst->getSampleCount(); id++) { + SampleType dstSample; + SampleType srcSample; + + is = s0 + filter.weightset[ip].src_offset; + + int iwFirst, iwLast; + if (is > 0) { + iwFirst = 0; + iwLast = tmin(filter.weightset[ip].n_weights, src.getSampleCount() - is); + } else { + iwFirst = -is; + is = 0; + iwLast = tmin(filter.weightset[ip].n_weights, src.getSampleCount()); + } + + double dstChannel[2]; + + dstChannel[0] = 0; + dstChannel[1] = 0; + + dstSample = SampleType(); + + for (iw = iwFirst; iw < iwLast; iw++, is++) { + weight = filter.weightset[ip].weight[iw]; + + srcSample = *(src.samples() + is); + + /* + T::SampleType tmp = srcSample*weight; + dstSample += tmp; + */ + + for (int i = 0; i < src.getChannelCount(); i++) + dstChannel[i] += (double)(srcSample.getValue(i) * weight); + + //assert(dstSample.getValue(0) == dstChannel[0]); + } + + for (int i = 0; i < src.getChannelCount(); i++) + dstSample.setValue(i, (ChannelValueType)(tround(dstChannel[i]))); + + *(dst->samples() + id) = dstSample; + + ip++; + if (ip == filter.dst_period) { + ip = 0; + s0 += filter.src_period; + } + } + + for (ip = 0; ip < filter.dst_period; ip++) + delete[] filter.weightset[ip].weight; + + delete[] filter.weightset; + return dst; +} + +//============================================================================= + +class TSoundTrackResample : public TSoundTransform +{ + TINT32 m_sampleRate; + FLT_TYPE m_filterType; + +public: + TSoundTrackResample(TINT32 sampleRate, FLT_TYPE filterType) + : TSoundTransform(), m_sampleRate(sampleRate), m_filterType(filterType) + { + } + + ~TSoundTrackResample(){}; + + TSoundTrackP compute(const TSoundTrackMono8Signed &src) + { + TSoundTrackMono8Signed *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackMono8Unsigned &src) + { + + TSoundTrackMono8Unsigned *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackStereo8Signed &src) + { + TSoundTrackStereo8Signed *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &src) + { + TSoundTrackStereo8Unsigned *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackMono16 &src) + { + TSoundTrackMono16 *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackStereo16 &src) + { + TSoundTrackStereo16 *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackMono24 &src) + { + TSoundTrackMono24 *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } + + TSoundTrackP compute(const TSoundTrackStereo24 &src) + { + TSoundTrackStereo24 *dst = resampleT( + const_cast(src), + m_sampleRate, m_filterType); + + return TSoundTrackP(dst); + } +}; + +//============================================================================== + +TSoundTrackP TSop::resample(TSoundTrackP src, TINT32 sampleRate) +{ + TSoundTrackResample *resample = new TSoundTrackResample(sampleRate, FLT_HAMMING3); + TSoundTrackP dst = src->apply(resample); + delete resample; + return dst; +} + +//============================================================================== + +template +TSoundTrackP doConvertWithoutResamplingT( + SRC *src, const TSoundTrackFormat &dstFormat) +{ + TSoundTrackP dst = TSoundTrack::create(dstFormat, src->getSampleCount()); + if (!dst) + return 0; + + TSoundTrackMono8Unsigned *dstM8U = + dynamic_cast(dst.getPointer()); + if (dstM8U) { + convertSamplesT(*dstM8U, *src); + return dstM8U; + } + + TSoundTrackMono8Signed *dstM8S = + dynamic_cast(dst.getPointer()); + if (dstM8S) { + convertSamplesT(*dstM8S, *src); + return dstM8S; + } + + TSoundTrackStereo8Signed *dstS8S = + dynamic_cast(dst.getPointer()); + if (dstS8S) { + convertSamplesT(*dstS8S, *src); + return dstS8S; + } + + TSoundTrackStereo8Unsigned *dstS8U = + dynamic_cast(dst.getPointer()); + if (dstS8U) { + convertSamplesT(*dstS8U, *src); + return dstS8U; + } + + TSoundTrackMono16 *dstM16 = + dynamic_cast(dst.getPointer()); + if (dstM16) { + convertSamplesT(*dstM16, *src); + return dstM16; + } + + TSoundTrackStereo16 *dstS16 = + dynamic_cast(dst.getPointer()); + if (dstS16) { + convertSamplesT(*dstS16, *src); + return dstS16; + } + + TSoundTrackMono24 *dstM24 = + dynamic_cast(dst.getPointer()); + if (dstM24) { + convertSamplesT(*dstM24, *src); + return dstM24; + } + + TSoundTrackStereo24 *dstS24 = + dynamic_cast(dst.getPointer()); + if (dstS24) { + convertSamplesT(*dstS24, *src); + return dstS24; + } + + return 0; +} + +//------------------------------------------------------------------------------ + +class TSoundTrackConverterWithoutResampling : public TSoundTransform +{ + + TSoundTrackFormat m_format; + +public: + TSoundTrackConverterWithoutResampling(const TSoundTrackFormat &format) + : m_format(format) {} + + ~TSoundTrackConverterWithoutResampling(){}; + + TSoundTrackP compute(const TSoundTrackMono8Signed &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackMono8Unsigned &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackStereo8Signed &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackMono16 &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackStereo16 &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + TSoundTrackP compute(const TSoundTrackMono24 &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } + + TSoundTrackP compute(const TSoundTrackStereo24 &src) + { + return doConvertWithoutResamplingT(&src, m_format); + } +}; + +//----------------------------------------------------------------------------- +namespace +{ + +void convertWithoutResampling(TSoundTrackP &dst, const TSoundTrackP &src) +{ + TSoundTrackConverterWithoutResampling *converter; + converter = new TSoundTrackConverterWithoutResampling(dst->getFormat()); + dst = src->apply(converter); + delete converter; +} + +} // namespace + +//============================================================================== + +TSoundTrackP TSop::convert( + const TSoundTrackP &src, + const TSoundTrackFormat &dstFormat) +{ + TINT32 dstSampleCount = + (TINT32)(src->getSampleCount() * (dstFormat.m_sampleRate / (double)src->getSampleRate())); + + TSoundTrackP dst = TSoundTrack::create(dstFormat, dstSampleCount); + TSop::convert(dst, src); + return dst; +} + +//------------------------------------------------------------------------------ + +void TSop::convert(TSoundTrackP &dst, const TSoundTrackP &src) +{ + int src_reslen, dst_reslen; + int src_bits, dst_bits; + int src_chans, dst_chans; + TSoundTrackP tmp, tmq; + RESORDER_TYPE order; + + assert(src->getSampleCount() >= 0 && dst->getSampleCount() >= 0); + if (!dst->getSampleCount()) + return; + + if (!src->getSampleCount()) { + dst->blank(0L, (TINT32)(dst->getSampleCount() - 1)); + return; + } + + if (src->getSampleRate() == dst->getSampleRate()) { + src_reslen = dst->getSampleCount(); + notMoreThan((int)src->getSampleCount(), src_reslen); + dst_reslen = src_reslen; + convertWithoutResampling(dst, src); + } else { + src_reslen = FULL_INT_MUL_DIV( + dst->getSampleCount(), + src->getSampleRate(), + dst->getSampleRate()); + + if (src_reslen > src->getSampleCount()) { + src_reslen = src->getSampleCount(); + dst_reslen = FULL_INT_MUL_DIV( + src_reslen, dst->getSampleRate(), src->getSampleRate()); + } else + dst_reslen = dst->getSampleCount(); + + src_chans = src->getChannelCount(); + dst_chans = dst->getChannelCount(); + src_bits = src->getBitPerSample(); + dst_bits = dst->getBitPerSample(); + + if (src_chans == dst_chans && src_bits == dst_bits) + if (src->isSampleSigned() == dst->isSampleSigned()) + order = RESORDER_RATE; + else + order = RESORDER_SIGN; + else if (src_chans < dst_chans) { + if (src_bits < dst_bits) + order = RESORDER_BITS_RATE_CHANS; + else + order = RESORDER_RATE_BITS_CHANS; + } else if (src_chans > dst_chans) { + if (src_bits > dst_bits) + order = RESORDER_CHANS_RATE_BITS; + else + order = RESORDER_CHANS_BITS_RATE; + } else { + if (src_bits > dst_bits) + order = RESORDER_RATE_BITS; + else + order = RESORDER_BITS_RATE; + } + + switch (order) { + case RESORDER_CHANS_RATE_BITS: + case RESORDER_BITS_RATE_CHANS: + int chans, bitPerSample; + if (src->getChannelCount() <= dst->getChannelCount()) + chans = src->getChannelCount(); + else + chans = dst->getChannelCount(); + + if (src->getBitPerSample() >= dst->getBitPerSample()) + bitPerSample = src->getBitPerSample(); + else + bitPerSample = dst->getBitPerSample(); + + tmp = TSoundTrack::create( + (int)src->getSampleRate(), + bitPerSample, + chans, + src_reslen * src->getSampleSize()); + + convertWithoutResampling(tmp, src); + tmq = TSop::resample(tmp, (TINT32)dst->getSampleRate()); + convertWithoutResampling(dst, tmq); + break; + + case RESORDER_RATE_BITS_CHANS: + case RESORDER_RATE_BITS: + case RESORDER_RATE_CHANS: + tmp = TSop::resample(src, (TINT32)dst->getSampleRate()); + convertWithoutResampling(dst, tmp); + break; + + case RESORDER_CHANS_BITS_RATE: + case RESORDER_BITS_RATE: + case RESORDER_CHANS_RATE: + case RESORDER_SIGN: + tmp = TSoundTrack::create( + (int)src->getSampleRate(), + dst->getBitPerSample(), + dst->getChannelCount(), + src_reslen * dst->getSampleSize(), + dst->isSampleSigned()); + + convertWithoutResampling(tmp, src); + dst = TSop::resample(tmp, (TINT32)dst->getSampleRate()); + break; + + case RESORDER_RATE: + dst = TSop::resample(src, (TINT32)dst->getSampleRate()); + break; + + default: + assert(false); + break; + } + } + + if (dst_reslen < dst->getSampleCount()) + dst->blank((TINT32)dst_reslen, (TINT32)(dst->getSampleCount() - 1)); +} + +//============================================================================== + +template +TSoundTrackP doReverb( + TSoundTrackT *src, + double delayTime, + double decayFactor, + double extendTime) +{ + TINT32 dstSampleCount = src->getSampleCount() + (TINT32)(src->getSampleRate() * extendTime); + + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), + src->getChannelCount(), + dstSampleCount); + + TINT32 sampleRate = (TINT32)src->getSampleRate(); + TINT32 k = (TINT32)(sampleRate * delayTime); + + T *srcSample = src->samples(); + T *dstSample = dst->samples(); + T *endDstSample = dst->samples() + k; + + while (dstSample < endDstSample) + *dstSample++ = *srcSample++; + + // la formula del reverb e' + // out(i) = in(i) + decayFactor * out(i - k) + + // int channelCount = src->getChannelCount(); + + endDstSample = dst->samples() + tmin(dstSampleCount, (TINT32)src->getSampleCount()); + while (dstSample < endDstSample) { + //*dstSample = *srcSample + *(dstSample - k)*decayFactor; + *dstSample = T::mix(*srcSample, 1, *(dstSample - k), decayFactor); + ++dstSample; + ++srcSample; + } + + endDstSample = dst->samples() + dstSampleCount; + while (dstSample < endDstSample) { + //*dstSample = *(dstSample - k)*decayFactor; + *dstSample = T::mix(T(), 0, *(dstSample - k), decayFactor); + ++dstSample; + } + + return TSoundTrackP(dst); +} + +//============================================================================== + +class TSoundReverb : public TSoundTransform +{ + double m_delayTime; + double m_decayFactor; + double m_extendTime; + +public: + TSoundReverb( + double delayTime, + double decayFactor, + double extendTime) + : m_delayTime(delayTime), m_decayFactor(decayFactor), m_extendTime(extendTime) + { + } + + ~TSoundReverb() {} + + virtual TSoundTrackP compute(const TSoundTrackMono8Signed &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackMono8Unsigned &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackStereo8Signed &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackStereo8Unsigned &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackMono16 &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackStereo16 &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackMono24 &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } + + virtual TSoundTrackP compute(const TSoundTrackStereo24 &src) + { + return doReverb( + const_cast(&src), + m_delayTime, + m_decayFactor, + m_extendTime); + } +}; + +//============================================================================== + +TSoundTrackP TSop::reverb( + TSoundTrackP src, + double delayTime, + double decayFactor, + double extendTime) +{ + TSoundReverb *reverb = new TSoundReverb(delayTime, decayFactor, extendTime); + assert(reverb); + if (!reverb) + return TSoundTrackP(); + TSoundTrackP dst = src->apply(reverb); + delete reverb; + return dst; +} + +//============================================================================== + +template +TSoundTrackP doGate( + TSoundTrackT *src, + double threshold, + double holdTime, + double /*releaseTime*/) +{ + + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), + src->getChannelCount(), + src->getSampleCount()); + + double sampleExcursion_inv = + 1.0 / (double)(src->getMaxPressure(0, src->getSampleCount() - 1, 0) - src->getMinPressure(0, src->getSampleCount() - 1, 0)); + + TINT32 holdTimeSamples = src->secondsToSamples(holdTime); + TINT32 time = 0; + + const T *srcSample = src->samples(); + const T *srcEndSample = srcSample + src->getSampleCount(); + T *dstSample = dst->samples(); + + while (srcSample < srcEndSample) { + if (fabs(srcSample->getValue(0) * sampleExcursion_inv) < threshold) { + if (time >= holdTimeSamples) + *dstSample = T(); + else + *dstSample = *srcSample; + + ++time; + } else { + time = 0; + *dstSample = *srcSample; + } + + ++srcSample; + ++dstSample; + } + + return dst; +} + +//============================================================================== + +class TSoundGate : public TSoundTransform +{ + double m_threshold; + double m_holdTime; + double m_releaseTime; + +public: + TSoundGate( + double threshold, + double holdTime, + double releaseTime) + : m_threshold(threshold), m_holdTime(holdTime), m_releaseTime(releaseTime) + { + } + + ~TSoundGate() {} + + TSoundTrackP compute(const TSoundTrackMono8Signed &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackMono8Unsigned &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackStereo8Signed &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackMono16 &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackStereo16 &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackMono24 &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } + + TSoundTrackP compute(const TSoundTrackStereo24 &src) + { + return doGate( + const_cast(&src), + m_threshold, + m_holdTime, + m_releaseTime); + } +}; + +//============================================================================== + +TSoundTrackP TSop::gate( + TSoundTrackP src, + double threshold, + double holdTime, + double releaseTime) +{ + TSoundGate *gate = new TSoundGate(threshold, holdTime, releaseTime); + assert(gate); + if (!gate) + return TSoundTrackP(); + TSoundTrackP dst = src->apply(gate); + delete gate; + return dst; +} + +//============================================================================== + +TSoundTrackP TSop::timeStrech(TSoundTrackP src, double ratio) +{ + TINT32 sampleRate = (TINT32)(src->getSampleRate() * ratio); + + if (sampleRate > 100000) + sampleRate = 100000; + + TSoundTrackP st; + + if (sampleRate > 0) { + TSoundTrackResample *resample = new TSoundTrackResample(sampleRate, FLT_TRIANGLE); + st = src->apply(resample); + delete resample; + st->setSampleRate(src->getSampleRate()); + } + + return st; +} + +//======================================================================================== + +template +TSoundTrackP doEcho( + TSoundTrackT *src, + double delayTime, + double decayFactor, + double extendTime) +{ + typedef typename T::ChannelValueType ChannelValueType; + + TINT32 dstSampleCount = src->getSampleCount() + (TINT32)(src->getSampleRate() * extendTime); + + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), + src->getChannelCount(), + dstSampleCount); + + TINT32 sampleRate = (TINT32)src->getSampleRate(); + TINT32 k = (TINT32)(sampleRate * delayTime); + + T *srcSample = src->samples(); + T *dstSample = dst->samples(); + T *endDstSample = dst->samples() + k; + + while (dstSample < endDstSample) + *dstSample++ = *srcSample++; + + // la formula dell'echo e' + // out(i) = in(i) + decayFactor * in(i - k) + + bool chans = src->getChannelCount() == 2; + endDstSample = dst->samples() + tmin(dstSampleCount, (TINT32)src->getSampleCount()); + while (dstSample < endDstSample) { + //*dstSample = *srcSample + *(srcSample - k)*decayFactor; + ChannelValueType val = + (ChannelValueType)((srcSample - k)->getValue(TSound::MONO) * decayFactor); + dstSample->setValue(TSound::MONO, srcSample->getValue(TSound::MONO) + val); + if (chans) { + ChannelValueType val = + (ChannelValueType)((srcSample - k)->getValue(TSound::RIGHT) * decayFactor); + dstSample->setValue(TSound::RIGHT, srcSample->getValue(TSound::RIGHT) + val); + } + ++dstSample; + ++srcSample; + } + + endDstSample = dstSample + k; + while (dstSample < endDstSample) { + //*dstSample = *(srcSample - k)*decayFactor; + ChannelValueType val = + (ChannelValueType)((srcSample - k)->getValue(TSound::MONO) * decayFactor); + dstSample->setValue(TSound::MONO, val); + if (chans) { + ChannelValueType val = + (ChannelValueType)((srcSample - k)->getValue(TSound::RIGHT) * decayFactor); + dstSample->setValue(TSound::RIGHT, val); + } + ++dstSample; + ++srcSample; + } + + srcSample = src->samples() + src->getSampleCount() - 1; + endDstSample = dst->samples() + dstSampleCount; + while (dstSample < endDstSample) { + //*dstSample = *(srcSample)*decayFactor; + ChannelValueType val = + (ChannelValueType)(srcSample->getValue(TSound::MONO) * decayFactor); + dstSample->setValue(TSound::MONO, val); + if (chans) { + ChannelValueType val = + (ChannelValueType)(srcSample->getValue(TSound::RIGHT) * decayFactor); + dstSample->setValue(TSound::RIGHT, val); + } + ++dstSample; + } + + return TSoundTrackP(dst); +} + +//------------------------------------------------------------------------------ + +void TSop::echo( + TSoundTrackP &dst, + const TSoundTrackP &src, + double delayTime, + double decayFactor, + double extendTime) +{ + TSoundTrackMono8Signed *srcM8S; + srcM8S = dynamic_cast(src.getPointer()); + if (srcM8S) + dst = doEcho(srcM8S, delayTime, decayFactor, extendTime); + else { + TSoundTrackMono8Unsigned *srcM8U; + srcM8U = dynamic_cast(src.getPointer()); + if (srcM8U) + dst = doEcho(srcM8U, delayTime, decayFactor, extendTime); + else { + TSoundTrackStereo8Signed *srcS8S; + srcS8S = dynamic_cast(src.getPointer()); + if (srcS8S) + dst = doEcho(srcS8S, delayTime, decayFactor, extendTime); + else { + TSoundTrackStereo8Unsigned *srcS8U; + srcS8U = dynamic_cast(src.getPointer()); + if (srcS8U) + dst = doEcho(srcS8U, delayTime, decayFactor, extendTime); + else { + TSoundTrackMono16 *srcM16; + srcM16 = dynamic_cast(src.getPointer()); + if (srcM16) + dst = doEcho(srcM16, delayTime, decayFactor, extendTime); + else { + TSoundTrackStereo16 *srcS16; + srcS16 = dynamic_cast(src.getPointer()); + if (srcS16) + dst = doEcho(srcS16, delayTime, decayFactor, extendTime); + else { + TSoundTrackMono24 *srcM24; + srcM24 = dynamic_cast(src.getPointer()); + if (srcM24) + dst = doEcho(srcM24, delayTime, decayFactor, extendTime); + else { + TSoundTrackStereo24 *srcS24; + srcS24 = dynamic_cast(src.getPointer()); + if (srcS24) + dst = doEcho(srcS24, delayTime, decayFactor, extendTime); + } + } + } + } + } + } + } +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::insertBlank(TSoundTrackP src, TINT32 s0, TINT32 len) +{ + assert(len >= 0); + if (len == 0) + return src; + + TINT32 ss0 = tcrop(s0, 0, src->getSampleCount()); + + TSoundTrackFormat format = src->getFormat(); + TSoundTrackP dst = TSoundTrack::create( + format, (src->getSampleCount() + len)); + + UCHAR *dstRawData = (UCHAR *)dst->getRawData(); + UCHAR *srcRawData = (UCHAR *)src->getRawData(); + + int bytePerSample = dst->getSampleSize(); + memcpy(dstRawData, srcRawData, ss0 * bytePerSample); + if (format.m_signedSample) + memset(dstRawData + ss0 * bytePerSample, 0, len * bytePerSample); + else + memset(dstRawData + ss0 * bytePerSample, 127, len * bytePerSample); + memcpy( + dstRawData + (ss0 + len) * bytePerSample, + srcRawData + ss0 * bytePerSample, + (src->getSampleCount() - ss0) * bytePerSample); + + return dst; +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::insertBlank(TSoundTrackP src, double t0, double len) +{ + return insertBlank(src, src->secondsToSamples(t0), src->secondsToSamples(len)); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::remove(TSoundTrackP src, TINT32 s0, TINT32 s1, TSoundTrackP &paste) +{ + TINT32 ss0, ss1; + + ss0 = tmax((TINT32)0, s0); + ss1 = tmin(s1, (TINT32)(src->getSampleCount() - 1)); + TSoundTrackP soundTrackSlice; + if (ss0 <= ss1) + soundTrackSlice = src->extract(ss0, ss1); + if (!soundTrackSlice) { + paste = TSoundTrackP(); + return src; + } + paste = soundTrackSlice->clone(); + + TSoundTrackFormat format = src->getFormat(); + TSoundTrackP dst = TSoundTrack::create(format, (src->getSampleCount() - (ss1 - ss0 + 1))); + + TINT32 dstSampleSize = dst->getSampleSize(); + UCHAR *newRowData = (UCHAR *)dst->getRawData(); + UCHAR *srcRowData = (UCHAR *)src->getRawData(); + + memcpy(newRowData, srcRowData, ss0 * dstSampleSize); + memcpy( + newRowData + (ss0 * dstSampleSize), + srcRowData + (ss1 + 1) * dstSampleSize, + (src->getSampleCount() - ss1 - 1) * dst->getSampleSize()); + + return dst; +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::remove(TSoundTrackP src, double t0, double t1, TSoundTrackP &paste) +{ + return remove(src, src->secondsToSamples(t0), src->secondsToSamples(t1), paste); +} + +//------------------------------------------------------------------------------ + +template +TSoundTrackP mixT( + TSoundTrackT *st1, double a1, TSoundTrackT *st2, double a2) +{ + TINT32 sampleCount = tmax(st1->getSampleCount(), st2->getSampleCount()); + + TSoundTrackT *dst = new TSoundTrackT( + st1->getSampleRate(), + st1->getChannelCount(), + sampleCount); + + T *dstSample = dst->samples(); + T *endDstSample = dstSample + tmin(st1->getSampleCount(), st2->getSampleCount()); + + T *st1Sample = st1->samples(); + T *st2Sample = st2->samples(); + + while (dstSample < endDstSample) { + *dstSample++ = T::mix(*st1Sample, a1, *st2Sample, a2); + ++st1Sample; + ++st2Sample; + } + + T *srcSample = st1->getSampleCount() > st2->getSampleCount() ? st1Sample : st2Sample; + endDstSample = dst->samples() + sampleCount; + while (dstSample < endDstSample) + *dstSample++ = *srcSample++; + + return TSoundTrackP(dst); +} + +//============================================================================= + +class TSoundTrackMixer : public TSoundTransform +{ + double m_alpha1, m_alpha2; + TSoundTrackP m_sndtrack; + +public: + TSoundTrackMixer(double a1, double a2, const TSoundTrackP &st2) + : TSoundTransform(), m_alpha1(a1), m_alpha2(a2), m_sndtrack(st2) + { + } + + ~TSoundTrackMixer(){}; + + TSoundTrackP compute(const TSoundTrackMono8Signed &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackMono8Unsigned &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackStereo8Signed &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackMono16 &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackStereo16 &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackMono24 &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } + + TSoundTrackP compute(const TSoundTrackStereo24 &src) + { + assert(src.getFormat() == m_sndtrack->getFormat()); + + return (mixT( + const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); + } +}; + +TSoundTrackP TSop::mix( + const TSoundTrackP &st1, + const TSoundTrackP &st2, + double a1, + double a2) +{ + TSoundTrackMixer *converter; + a1 = tcrop(a1, 0.0, 1.0); + a2 = tcrop(a2, 0.0, 1.0); + converter = new TSoundTrackMixer(a1, a2, st2); + TSoundTrackP snd = st1->apply(converter); + delete converter; + return (snd); +} + +//============================================================================== +// +// TSop::FadeIn +// +//============================================================================== + +template +TSoundTrackP doFadeIn(const TSoundTrackT &track, double riseFactor) +{ + typedef typename T::ChannelValueType ChannelValueType; + int sampleCount = (int)((double)track.getSampleCount() * riseFactor); + if (!sampleCount) + sampleCount = 1; + assert(sampleCount); + int channelCount = track.getChannelCount(); + + TSoundTrackT *out = + new TSoundTrackT(track.getSampleRate(), channelCount, sampleCount); + + double val[2], step[2]; + + ChannelValueType chan[2]; + const T *firstSample = track.samples(); + for (int k = 0; k < channelCount; ++k) { + chan[k] = firstSample->getValue(k); + if (firstSample->isSampleSigned()) { + val[k] = 0; + step[k] = (double)chan[k] / (double)sampleCount; + } else { + val[k] = 127; + step[k] = (double)(chan[k] - 128) / (double)sampleCount; + } + } + + T *psample = out->samples(); + T *end = psample + out->getSampleCount(); + + while (psample < end) { + T sample; + for (int k = 0; k < channelCount; ++k) { + sample.setValue(k, (ChannelValueType)val[k]); + val[k] += step[k]; + } + *psample = sample; + ++psample; + } + + return out; +} + +//------------------------------------------------------------------------------ + +class TSoundTrackFaderIn : public TSoundTransform +{ +public: + TSoundTrackFaderIn(double riseFactor) + : TSoundTransform(), m_riseFactor(riseFactor) + { + } + + TSoundTrackP compute(const TSoundTrackMono8Signed &); + TSoundTrackP compute(const TSoundTrackStereo8Signed &); + TSoundTrackP compute(const TSoundTrackMono8Unsigned &); + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &); + TSoundTrackP compute(const TSoundTrackMono16 &); + TSoundTrackP compute(const TSoundTrackStereo16 &); + TSoundTrackP compute(const TSoundTrackMono24 &); + TSoundTrackP compute(const TSoundTrackStereo24 &); + + double m_riseFactor; +}; + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono8Signed &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackStereo8Signed &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono8Unsigned &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackStereo8Unsigned &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono16 &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackStereo16 &track) +{ + return doFadeIn(track, m_riseFactor); +} +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono24 &track) +{ + return doFadeIn(track, m_riseFactor); +} +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackStereo24 &track) +{ + return doFadeIn(track, m_riseFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::fadeIn(const TSoundTrackP src, double riseFactor) +{ + TSoundTrackFaderIn *fader = new TSoundTrackFaderIn(riseFactor); + TSoundTrackP out = src->apply(fader); + delete fader; + return out; +} + +//============================================================================== +// +// TSop::FadeOut +// +//============================================================================== + +template +TSoundTrackP doFadeOut( + const TSoundTrackT &track, double decayFactor) +{ + typedef typename T::ChannelValueType ChannelValueType; + int sampleCount = (int)((double)track.getSampleCount() * decayFactor); + if (!sampleCount) + sampleCount = 1; + assert(sampleCount); + int channelCount = track.getChannelCount(); + + TSoundTrackT *out = + new TSoundTrackT(track.getSampleRate(), channelCount, sampleCount); + + double val[2], step[2]; + ChannelValueType chan[2]; + const T *lastSample = (track.samples() + track.getSampleCount() - 1); + for (int k = 0; k < channelCount; ++k) { + chan[k] = lastSample->getValue(k); + val[k] = (double)chan[k]; + if (lastSample->isSampleSigned()) + step[k] = (double)chan[k] / (double)sampleCount; + else + step[k] = (double)(chan[k] - 128) / (double)sampleCount; + } + + T *psample = out->samples(); + T *end = psample + out->getSampleCount(); + + while (psample < end) { + T sample; + for (int k = 0; k < channelCount; ++k) { + sample.setValue(k, (ChannelValueType)val[k]); + val[k] -= step[k]; + } + *psample = sample; + ++psample; + } + + return out; +} + +//------------------------------------------------------------------------------ + +class TSoundTrackFaderOut : public TSoundTransform +{ +public: + TSoundTrackFaderOut(double decayFactor) + : TSoundTransform(), m_decayFactor(decayFactor) + { + } + + TSoundTrackP compute(const TSoundTrackMono8Signed &); + TSoundTrackP compute(const TSoundTrackStereo8Signed &); + TSoundTrackP compute(const TSoundTrackMono8Unsigned &); + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &); + TSoundTrackP compute(const TSoundTrackMono16 &); + TSoundTrackP compute(const TSoundTrackStereo16 &); + TSoundTrackP compute(const TSoundTrackMono24 &); + TSoundTrackP compute(const TSoundTrackStereo24 &); + + double m_decayFactor; +}; + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono8Signed &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo8Signed &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono8Unsigned &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo8Unsigned &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono16 &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo16 &track) +{ + return doFadeOut(track, m_decayFactor); +} +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono24 &track) +{ + return doFadeOut(track, m_decayFactor); +} +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo24 &track) +{ + return doFadeOut(track, m_decayFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::fadeOut(const TSoundTrackP src, double decayFactor) +{ + TSoundTrackFaderOut *fader = new TSoundTrackFaderOut(decayFactor); + TSoundTrackP out = src->apply(fader); + delete fader; + return out; +} + +//============================================================================== +// +// TSop::CrossFade +// +//============================================================================== + +template +TSoundTrackP doCrossFade( + const TSoundTrackT &track1, + TSoundTrackT *track2, + double crossFactor) +{ + typedef typename T::ChannelValueType ChannelValueType; + int channelCount = track2->getChannelCount(); + int sampleCount = (int)((double)track2->getSampleCount() * crossFactor); + if (!sampleCount) + sampleCount = 1; + assert(sampleCount); + + //ultimo campione di track1 + ChannelValueType chanTrack1[2]; + const T *lastSample = (track1.samples() + track1.getSampleCount() - 1); + int k; + for (k = 0; k < channelCount; ++k) + chanTrack1[k] = lastSample->getValue(k); + + double val[2], step[2]; + + //primo campione di track2 + ChannelValueType chanTrack2[2]; + const T *firstSample = track2->samples(); + for (k = 0; k < channelCount; ++k) { + chanTrack2[k] = firstSample->getValue(k); + val[k] = chanTrack1[k] - chanTrack2[k]; + step[k] = val[k] / (double)sampleCount; + } + + TSoundTrackT *out = + new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCount); + + T *psample = out->samples(); + T *end = psample + out->getSampleCount(); + + while (psample < end) { + T sample; + for (int k = 0; k < channelCount; ++k) { + double tot = (double)firstSample->getValue(k) + val[k]; + ChannelValueType value = (ChannelValueType)tot; + + sample.setValue(k, value); + val[k] -= step[k]; + } + *psample = sample; + ++psample; + //++firstSample; + } + + return out; +} + +//------------------------------------------------------------------------------ + +class TSoundTrackCrossFader : public TSoundTransform +{ +public: + TSoundTrackCrossFader(TSoundTrackP src, double crossFactor) + : TSoundTransform(), m_st(src), m_crossFactor(crossFactor) + { + } + + TSoundTrackP compute(const TSoundTrackMono8Signed &); + TSoundTrackP compute(const TSoundTrackStereo8Signed &); + TSoundTrackP compute(const TSoundTrackMono8Unsigned &); + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &); + TSoundTrackP compute(const TSoundTrackMono16 &); + TSoundTrackP compute(const TSoundTrackStereo16 &); + TSoundTrackP compute(const TSoundTrackMono24 &); + TSoundTrackP compute(const TSoundTrackStereo24 &); + + TSoundTrackP m_st; + double m_crossFactor; +}; + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono8Signed &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackStereo8Signed &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono8Unsigned &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackStereo8Unsigned &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono16 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackStereo16 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono24 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackStereo24 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFade( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::crossFade( + const TSoundTrackP src1, const TSoundTrackP src2, double crossFactor) +{ + TSoundTrackCrossFader *fader = new TSoundTrackCrossFader(src2, crossFactor); + TSoundTrackP out = src1->apply(fader); + delete fader; + return out; +} + +// +// +// +// +//============================================================================== +// +// TSop::CrossFadeOverWrite +// +//============================================================================== + +template +TSoundTrackP doCrossFadeOverWrite( + const TSoundTrackT &track1, + TSoundTrackT *track2, + double crossFactor) +{ + typedef typename T::ChannelValueType ChannelValueType; + int channelCount = track2->getChannelCount(); + int sampleCount = (int)((double)track2->getSampleCount() * crossFactor); + int sampleCountT2 = track2->getSampleCount(); + + if (sampleCount == 0 && sampleCountT2 == 1) + return track2; + if (sampleCount == 0) + sampleCount = 1; + assert(sampleCount); + + //ultimo campione di track1 + ChannelValueType chanTrack1[2]; + const T *lastSample = (track1.samples() + track1.getSampleCount() - 1); + int k; + for (k = 0; k < channelCount; ++k) + chanTrack1[k] = lastSample->getValue(k); + + double val[2], step[2]; + + //primo campione di track2 + ChannelValueType chanTrack2[2]; + const T *firstSample = track2->samples() + sampleCount; + for (k = 0; k < channelCount; ++k) { + chanTrack2[k] = firstSample->getValue(k); + val[k] = chanTrack1[k] - chanTrack2[k]; + step[k] = val[k] / (double)sampleCount; + } + + TSoundTrackT *out = + new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCountT2); + + T *psample = out->samples(); + T *end = psample + sampleCount; + + while (psample < end) { + T sample; + for (int k = 0; k < channelCount; ++k) { + double tot = (double)firstSample->getValue(k) + val[k]; + ChannelValueType value = (ChannelValueType)tot; + + sample.setValue(k, value); + val[k] -= step[k]; + } + *psample = sample; + ++psample; + } + + out->copy(track2->extract(sampleCount, sampleCountT2 - 1), sampleCount); + + return out; +} + +//------------------------------------------------------------------------------ + +class TSoundTrackCrossFaderOverWrite : public TSoundTransform +{ +public: + TSoundTrackCrossFaderOverWrite(TSoundTrackP src, double crossFactor) + : TSoundTransform(), m_st(src), m_crossFactor(crossFactor) + { + } + + TSoundTrackP compute(const TSoundTrackMono8Signed &); + TSoundTrackP compute(const TSoundTrackStereo8Signed &); + TSoundTrackP compute(const TSoundTrackMono8Unsigned &); + TSoundTrackP compute(const TSoundTrackStereo8Unsigned &); + TSoundTrackP compute(const TSoundTrackMono16 &); + TSoundTrackP compute(const TSoundTrackStereo16 &); + TSoundTrackP compute(const TSoundTrackMono24 &); + TSoundTrackP compute(const TSoundTrackStereo24 &); + + TSoundTrackP m_st; + double m_crossFactor; +}; + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackMono8Signed &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackStereo8Signed &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackMono8Unsigned &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackStereo8Unsigned &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackMono16 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackStereo16 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackMono24 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackCrossFaderOverWrite::compute(const TSoundTrackStereo24 &src) +{ + assert(src.getFormat() == m_st->getFormat()); + return doCrossFadeOverWrite( + src, dynamic_cast(m_st.getPointer()), m_crossFactor); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSop::crossFade(double crossFactor, + const TSoundTrackP src1, const TSoundTrackP src2) +{ + TSoundTrackCrossFaderOverWrite *fader = new TSoundTrackCrossFaderOverWrite(src2, crossFactor); + TSoundTrackP out = src1->apply(fader); + delete fader; + return out; +} diff --git a/toonz/sources/common/tsound/tsound.cpp b/toonz/sources/common/tsound/tsound.cpp new file mode 100644 index 0000000..c95addc --- /dev/null +++ b/toonz/sources/common/tsound/tsound.cpp @@ -0,0 +1,350 @@ + + +#include "tsound.h" +#include "tsound_t.h" +#include "tconvert.h" + +#define TRK_M8 9 +#define TRK_S8 10 +#define TRK_M16 17 +#define TRK_S16 18 +#define TRK_M24 25 +#define TRK_S24 26 + +//============================================================================== + +DEFINE_CLASS_CODE(TSoundTrack, 12) + +//------------------------------------------------------------------------------ + +TSoundTrack::TSoundTrack() + : TSmartObject(m_classCode), m_parent(0), m_buffer(0), m_bufferOwner(false) +{ +} + +//------------------------------------------------------------------------------ + +TSoundTrack::TSoundTrack(TUINT32 sampleRate, + int bitPerSample, + int channelCount, + int sampleSize, + TINT32 sampleCount, + bool isSampleSigned) + + : TSmartObject(m_classCode), m_sampleRate(sampleRate), m_sampleSize(sampleSize), m_bitPerSample(bitPerSample), m_sampleCount(sampleCount), m_channelCount(channelCount), m_parent(0), m_bufferOwner(true) +{ + m_buffer = (UCHAR *)malloc(sampleCount * m_sampleSize); + if (!m_buffer) + return; + + //m_buffer = new UCHAR[sampleCount*m_sampleSize]; + if (isSampleSigned) + memset(m_buffer, 0, sampleCount * sampleSize); + else + memset(m_buffer, 127, sampleCount * sampleSize); +} + +//------------------------------------------------------------------------------ + +TSoundTrack::TSoundTrack(TUINT32 sampleRate, + int bitPerSample, + int channelCount, + int sampleSize, + TINT32 sampleCount, + UCHAR *buffer, + TSoundTrack *parent) + + : TSmartObject(m_classCode), m_sampleRate(sampleRate), m_sampleSize(sampleSize), m_bitPerSample(bitPerSample), m_sampleCount(sampleCount), m_channelCount(channelCount), m_parent(parent), m_buffer(buffer), m_bufferOwner(false) +{ + if (m_parent) + m_parent->addRef(); +} + +//------------------------------------------------------------------------------ + +TSoundTrack::~TSoundTrack() +{ + if (m_parent) + m_parent->release(); + //if (m_buffer && m_bufferOwner) delete [] m_buffer; + if (m_buffer && m_bufferOwner) + free(m_buffer); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrack::create( + TUINT32 sampleRate, int bitPerSample, + int channelCount, TINT32 sampleCount, + bool signedSample) +{ + TSoundTrackP st; + int type = bitPerSample + channelCount; + switch (type) { + case TRK_M8: + if (signedSample) + st = new TSoundTrackMono8Signed( + sampleRate, channelCount, sampleCount); + else + st = new TSoundTrackMono8Unsigned( + sampleRate, channelCount, sampleCount); + break; + case TRK_S8: + if (signedSample) + st = new TSoundTrackStereo8Signed( + sampleRate, channelCount, sampleCount); + else + st = new TSoundTrackStereo8Unsigned( + sampleRate, channelCount, sampleCount); + break; + + case TRK_M16: + st = new TSoundTrackMono16( + sampleRate, channelCount, sampleCount); + break; + + case TRK_S16: + st = new TSoundTrackStereo16( + sampleRate, channelCount, sampleCount); + break; + + case TRK_M24: + st = new TSoundTrackMono24( + sampleRate, channelCount, sampleCount); + break; + + case TRK_S24: + st = new TSoundTrackStereo24( + sampleRate, channelCount, sampleCount); + break; + + default: + string s; + s = "Type " + toString((int)sampleRate) + " Hz " + toString(bitPerSample) + " bits "; + if (channelCount == 1) + s += "mono: "; + else + s += "stereo: "; + s += "Unsupported\n"; + + throw TException(s); + } + + if (!st->getRawData()) { + return 0; + } + return st; +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrack::create( + TUINT32 sampleRate, int bitPerSample, + int channelCount, TINT32 sampleCount, void *buffer, + bool signedSample) +{ + TSoundTrackP st; + int type = bitPerSample + channelCount; + switch (type) { + case TRK_M8: + if (signedSample) + st = new TSoundTrackMono8Signed( + sampleRate, channelCount, sampleCount, (TMono8SignedSample *)buffer, 0); + else + st = new TSoundTrackMono8Unsigned( + sampleRate, channelCount, sampleCount, (TMono8UnsignedSample *)buffer, 0); + break; + case TRK_S8: + if (signedSample) + st = new TSoundTrackStereo8Signed( + sampleRate, channelCount, sampleCount, (TStereo8SignedSample *)buffer, 0); + else + st = new TSoundTrackStereo8Unsigned( + sampleRate, channelCount, sampleCount, (TStereo8UnsignedSample *)buffer, 0); + break; + + case TRK_M16: + st = new TSoundTrackMono16( + sampleRate, channelCount, sampleCount, (TMono16Sample *)buffer, 0); + break; + + case TRK_S16: + st = new TSoundTrackStereo16( + sampleRate, channelCount, sampleCount, (TStereo16Sample *)buffer, 0); + break; + + case TRK_M24: + st = new TSoundTrackMono24( + sampleRate, channelCount, sampleCount, (TMono24Sample *)buffer, 0); + break; + + case TRK_S24: + st = new TSoundTrackStereo24( + sampleRate, channelCount, sampleCount, (TStereo24Sample *)buffer, 0); + break; + + default: + string s; + s = "Type " + toString((int)sampleRate) + " Hz " + toString(bitPerSample) + " bits "; + if (channelCount == 1) + s += "mono: "; + else + s += "stereo: "; + s += "Unsupported\n"; + + throw TException(s); + } + + return st; +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrack::create(const TSoundTrackFormat &format, TINT32 sampleCount, void *buffer) +{ + return TSoundTrack::create( + (int)format.m_sampleRate, + format.m_bitPerSample, + format.m_channelCount, + sampleCount, + buffer, + format.m_signedSample); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrack::create(const TSoundTrackFormat &format, TINT32 sampleCount) +{ + return TSoundTrack::create( + (int)format.m_sampleRate, + format.m_bitPerSample, + format.m_channelCount, + sampleCount, + format.m_signedSample); +} + +//------------------------------------------------------------------------------ + +TINT32 TSoundTrack::secondsToSamples(double s) const +{ + double dsamp = s * m_sampleRate; + TINT32 lsamp = (TINT32)dsamp; + if ((double)lsamp < dsamp - TConsts::epsilon) + lsamp++; + return lsamp; +} + +//------------------------------------------------------------------------------ + +double TSoundTrack::samplesToSeconds(TINT32 f) const +{ + return f / (double)m_sampleRate; +} + +//============================================================================== + +bool TSoundTrackFormat::operator==(const TSoundTrackFormat &rhs) +{ + return (m_sampleRate == rhs.m_sampleRate && + m_bitPerSample == rhs.m_bitPerSample && + m_channelCount == rhs.m_channelCount && + m_signedSample == rhs.m_signedSample); +} + +//------------------------------------------------------------------------------ + +bool TSoundTrackFormat::operator!=(const TSoundTrackFormat &rhs) +{ + return !operator==(rhs); +} + +//============================================================================== + +double TSoundTrack::getDuration() const +{ + return samplesToSeconds(m_sampleCount); +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundTrack::getFormat() const +{ + return TSoundTrackFormat( + getSampleRate(), + getBitPerSample(), + getChannelCount(), + isSampleSigned()); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrack::extract(double t0, double t1) +{ + return extract(secondsToSamples(t0), secondsToSamples(t1)); +} + +//------------------------------------------------------------------------------ + +void TSoundTrack::copy(const TSoundTrackP &src, double dst_t0) +{ + copy(src, secondsToSamples(dst_t0)); +} + +//------------------------------------------------------------------------------ + +void TSoundTrack::blank(double t0, double t1) +{ + blank(secondsToSamples(t0), secondsToSamples(t1)); +} +//------------------------------------------------------------------------------ + +double TSoundTrack::getPressure(double second, TSound::Channel chan) const +{ + return getPressure(secondsToSamples(second), chan); +} + +//------------------------------------------------------------------------------ + +void TSoundTrack::getMinMaxPressure( + double t0, double t1, TSound::Channel chan, + double &min, double &max) const +{ + getMinMaxPressure(secondsToSamples(t0), secondsToSamples(t1), chan, min, max); +} + +//------------------------------------------------------------------------------ + +void TSoundTrack::getMinMaxPressure(TSound::Channel chan, double &min, double &max) const +{ + getMinMaxPressure(0, (TINT32)(getSampleCount() - 1), chan, min, max); +} + +//------------------------------------------------------------------------------ + +double TSoundTrack::getMaxPressure(double t0, double t1, TSound::Channel chan) const +{ + return getMaxPressure(secondsToSamples(t0), secondsToSamples(t1), chan); +} + +//------------------------------------------------------------------------------ + +double TSoundTrack::getMaxPressure(TSound::Channel chan) const +{ + return getMaxPressure(0, (TINT32)(getSampleCount() - 1), chan); +} + +//------------------------------------------------------------------------------ + +double TSoundTrack::getMinPressure(double t0, double t1, TSound::Channel chan) const +{ + return getMinPressure(secondsToSamples(t0), secondsToSamples(t1), chan); +} + +//------------------------------------------------------------------------------ + +double TSoundTrack::getMinPressure(TSound::Channel chan) const +{ + return getMinPressure(0, (TINT32)(getSampleCount() - 1), chan); +} + +//------------------------------------------------------------------------------ diff --git a/toonz/sources/common/tsound/tsound_io.cpp b/toonz/sources/common/tsound/tsound_io.cpp new file mode 100644 index 0000000..349879e --- /dev/null +++ b/toonz/sources/common/tsound/tsound_io.cpp @@ -0,0 +1,130 @@ + + +#include "tsound_io.h" +#include "tconvert.h" + +DEFINE_CLASS_CODE(TSoundTrackReader, 13) +DEFINE_CLASS_CODE(TSoundTrackWriter, 14) + +//----------------------------------------------------------- + +std::map SoundTrackReaderTable; +std::map SoundTrackWriterTable; + +//----------------------------------------------------------- + +TSoundTrackReader::TSoundTrackReader(const TFilePath &fp) + : TSmartObject(m_classCode), m_path(fp) +{ +} + +//----------------------------------------------------------- + +TSoundTrackReader::~TSoundTrackReader() {} + +//=========================================================== + +TSoundTrackReaderP::TSoundTrackReaderP(const TFilePath &path) +{ + QString type = QString::fromStdString(toLower(path.getType())); + std::map::iterator it; + it = SoundTrackReaderTable.find(type); + if (it != SoundTrackReaderTable.end()) { + m_pointer = it->second(path); + assert(m_pointer); + m_pointer->addRef(); + } else { + m_pointer = 0; + throw TException(path.getWideString() + L": soundtrack reader not implemented"); + } +} + +//=========================================================== + +TSoundTrackWriter::TSoundTrackWriter(const TFilePath &fp) + : TSmartObject(m_classCode), m_path(fp) +{ +} + +//----------------------------------------------------------- + +TSoundTrackWriter::~TSoundTrackWriter() {} + +//=========================================================== + +TSoundTrackWriterP::TSoundTrackWriterP(const TFilePath &path) +{ + QString type = QString::fromStdString(toLower(path.getType())); + std::map::iterator it; + it = SoundTrackWriterTable.find(type); + if (it != SoundTrackWriterTable.end()) { + m_pointer = it->second(path); + assert(m_pointer); + m_pointer->addRef(); + } else { + m_pointer = 0; + throw TException(path.getWideString() + L"soundtrack writer not implemented"); + } +} + +//============================================================ +// +// Helper functions statiche +// +//============================================================ + +bool TSoundTrackReader::load(const TFilePath &path, TSoundTrackP &st) +{ + st = TSoundTrackReaderP(path)->load(); + return st; +} + +//----------------------------------------------------------- + +void TSoundTrackReader::getSupportedFormats(QStringList &names) +{ + for (std::map::iterator it = SoundTrackReaderTable.begin(); + it != SoundTrackReaderTable.end(); + ++it) { + names.push_back(it->first); + } +} +//----------------------------------------------------------- + +bool TSoundTrackWriter::save(const TFilePath &path, const TSoundTrackP &st) +{ + return TSoundTrackWriterP(path)->save(st); +} + +//----------------------------------------------------------- + +void TSoundTrackWriter::getSupportedFormats(QStringList &names) +{ + for (std::map::iterator it = SoundTrackWriterTable.begin(); + it != SoundTrackWriterTable.end(); + ++it) { + names.push_back(it->first); + } +} + +//=========================================================== +// +// funzioni per la registrazione dei formati (chiamate dal Plugin) +// +//=========================================================== + +void TSoundTrackReader::define( + QString extension, + TSoundTrackReaderCreateProc *proc) +{ + SoundTrackReaderTable[extension] = proc; +} + +//----------------------------------------------------------- + +void TSoundTrackWriter::define( + QString extension, + TSoundTrackWriterCreateProc *proc) +{ + SoundTrackWriterTable[extension] = proc; +} diff --git a/toonz/sources/common/tsound/tsound_l.cpp b/toonz/sources/common/tsound/tsound_l.cpp new file mode 100644 index 0000000..f922ff3 --- /dev/null +++ b/toonz/sources/common/tsound/tsound_l.cpp @@ -0,0 +1,2251 @@ + + +#include "tfilepath.h" +#include "tsound.h" +#include "tsound_io.h" +#include "tsop.h" +#include "tthread.h" +#include "texception.h" +#include "tsystem.h" +#include +#include +#include +#include + +#include + +#include +#include + +//using namespace std; + +//forward declaration +namespace +{ +int openMixer(); +int getCurrentRecordSource(int mixer); +bool writeVolume(int volume, int mixer, int indexDev); +bool selectInputDevice(TSoundInputDevice::Source dev); +string parseError(int error); +} + +// bisogna interagire con /dev/dsp per il settaggio delle +// caratteristiche della traccia tipo bit, rate, canali e +// per effettuare lettura/scrittura ossia record/play +// +// mentre bisogna interagire con /dev/mixer per modificare +// i valori del volume e per selezionare il dispositivo da +// cui registrare e ascoltare + +class SmartWatch +{ + struct timeval m_start_tv; + TINT32 m_totalus; + bool m_stopped; + +public: + SmartWatch() : m_totalus(0), m_stopped(true) + { + timerclear(&m_start_tv); + } + void start() + { + m_stopped = false; + gettimeofday(&m_start_tv, 0); + } + void stop() + { + m_stopped = true; + struct timeval tv; + gettimeofday(&tv, 0); + m_totalus = (tv.tv_sec - m_start_tv.tv_sec) * 1000000 + (tv.tv_usec - m_start_tv.tv_usec); + } + double getTotalTime() + { + if (!m_stopped) //questa e' una porcata! + { + stop(); + m_stopped = false; + } + return m_totalus / 1000.; + } + + void addDelay(double ms) + { + m_start_tv.tv_usec += (long)(ms * 1000.); + } +}; + +//====================================================================== +//====================================================================== +// CLASSI PER IL PLAYBACK +//====================================================================== +//====================================================================== +class TSoundOutputDeviceImp +{ +private: + static int m_count; + +public: + int m_dev; + bool m_stopped; + bool m_isPlaying; + bool m_looped; + TSoundTrackFormat m_currentFormat; + std::set m_supportedRate; + static std::multimap m_supportFormats; + + typedef pair WaitPair; + vector m_waitingTracks; + std::set m_listeners; + TThread::Executor m_executor; + TThread::Mutex m_mutex; + + TSoundOutputDeviceImp() + : m_dev(-1), m_stopped(false), m_isPlaying(false), m_looped(false), m_supportedRate() + { + /* + if (m_count != 0) + throw TException("unable to create second instance of TSoundOutputDeviceImp"); +*/ + ++m_count; + checkSupportedFormat(); + }; + + ~TSoundOutputDeviceImp() { --m_count; }; + + bool doOpenDevice(); + bool doCloseDevice(); + bool verifyRate(); + void insertAllRate(); + void checkSupportedFormat(); + bool isSupportFormat(const TSoundTrackFormat &fmt); + void setFormat(const TSoundTrackFormat &fmt); +}; + +int TSoundOutputDeviceImp::m_count = 0; +std::multimap TSoundOutputDeviceImp::m_supportFormats; +//---------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::doOpenDevice() +{ + if (m_dev >= 0) + return true; + + TThread::ScopedLock sl(m_mutex); + m_dev = open("/dev/dsp", O_WRONLY, 0); + if (m_dev < 0) { + string errMsg = strerror(errno); + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, errMsg + " /dev/dsp" + /*"Unable to open device /dev/dsp; check permissions"*/); + } + + //le chiamate a questa ioctl sono state commentate perche' pag 36 doc OSS + //"this ioctl stop the device immadiately and returns it to a state where it + //can accept new parameters. It Should not be called after opening the device + //as it may cause unwanted side effect in his situation. require to abort play + //or secord.Generally recommended to open and close device after using the RESET" + //ioctl(m_dev, SNDCTL_DSP_RESET,0); + + // N.B. e' bene che la dimensione sia piccola cosi' l'attesa, in seguito + // alla richiesta di stop, + // e' minore in questo caso vogliamo 32 frammenti ognuno di 256 byte + // Se non chiamata questa ioctl il device la calcola per conto suo + // ma alcune volte la dimensione dei frammenti potrebbe essere eccessiva e + // creare dei click e dei silenzi in attesi ad esempio nel playback dentro + // zcomp, quindi e' meglio settarla. 32 rappresenta il numero di frammenti + // di solito e' documentato che 2 siano sufficienti ma potrebbero essere pochi + // soprattutto se si interagisce con altre console + //int fraginfo = ( 32<<16)|8; + + int fraginfo = 0xffffffff; + if (ioctl(m_dev, SNDCTL_DSP_SETFRAGMENT, &fraginfo) == -1) + perror("SETFRAGMENT"); + //if(fraginfo != ((32<<16)|8)) + //std::cout << std::hex << fraginfo<::iterator it; + + for (it = m_supportedRate.begin(); + it != m_supportedRate.end(); ++it) { + int sampleRate = *it; + if (ioctl(m_dev, SNDCTL_DSP_SPEED, &sampleRate) == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified sample rate aaaa"); + if (sampleRate != *it) + m_supportedRate.erase(*it); + } + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDeviceImp::checkSupportedFormat() +{ + if (!m_supportFormats.empty()) + return; + int test_formats[] = { + AFMT_U8, + AFMT_S8, + AFMT_S16_LE, + AFMT_S16_BE, + /* // servono per supportare traccie a 24 e 32 bit ma non compaiono in + // nessun file linux/soundcard.h in distribuzione sulle macchine che abbiamo + // il che fa pensare che non sono supportati ancora + AFMT_S32_LE, + AFMT_S32_BE, +*/ + 0}; + + int test_channels[] = { + 1, + 2, + 0}; + + TUINT32 test_sample_rates[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 0}; + + // Open the device nonblocking, so the open call returns immediately. + if (m_dev) + if ((m_dev = open("/dev/dsp", O_WRONLY | O_NONBLOCK)) == -1) { + /* + m_dev = -1; + string errMsg = strerror(errno); + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, errMsg + " /dev/dsp\n: impossible check supported formats"); + */ + return; + } + + int mask; + // Querying hardware supported formats. + if (ioctl(m_dev, SNDCTL_DSP_GETFMTS, &mask) == -1) { + /* + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Getting supported formats failed."); + */ + return; + } else { + for (int i = 0; test_formats[i] != 0; i++) { + // Test all the formats in test_formats which are supported by the hardware. + + if (mask & test_formats[i]) { + // Test the format only if it is supported by the hardware. + // Note: We also could test formats that are not supported by the hardware. + // In some cases there exist OSS software converter, so that some formats + // work but are not reported by the above SNDCTL_DSP_GETFMTS. + + int fmt = test_formats[i]; + // Try to set the format... + if (ioctl(m_dev, SNDCTL_DSP_SETFMT, &fmt) == -1) + continue; //gli altri formati potrebbero essere supportati + else { + // and always check the variable after doing an ioctl! + if (fmt == test_formats[i]) { + // Test the supported channel numbers for this format. + // Note: If you need a channel that is not tested here, simply add it to + // the definition of the array test_channels in this file. + for (int j = 0; test_channels[j] != 0; j++) { + int test_channel = test_channels[j]; + + // Try to set the channel number. + if (ioctl(m_dev, SNDCTL_DSP_CHANNELS, &test_channel) == -1) + continue; //altri canali potrebbero essere supportati + else { + if (test_channel == test_channels[j]) { + // Last step: Test the supported sample rates for the current channel number + // and format. + // Note: If you need a sample rate that is not tested here, simply add it to + // the definition of the array test_sample_rates in this file. + for (int k = 0; test_sample_rates[k] != 0; k++) { + TUINT32 test_rate = test_sample_rates[k]; + if (ioctl(m_dev, SNDCTL_DSP_SPEED, &test_rate) == -1) + continue; //altri rates ppotrebbero essere supportati + else { + bool sign = true; + int bits; + + if (fmt == AFMT_U8 || fmt == AFMT_S8) { + bits = 8; + if (fmt == AFMT_U8) + sign = false; + } else if (fmt == AFMT_S16_LE || fmt == AFMT_S16_BE) + bits = 16; + /*// vedi commento alla variabile test_formats + else if(fmt == AFMT_S32_LE || fmt == AFMT_S32_BE) + bits = 24; + */ + // Add it to the format in the input property. + //std::cout << test_rate << " " <::iterator it; + pair::iterator, std::multimap::iterator> findRange; + findRange = m_supportFormats.equal_range(fmt.m_sampleRate); + + it = findRange.first; + for (; it != findRange.second; ++it) { + assert(it->first == fmt.m_sampleRate); + if (it->second == fmt) { + return true; + } + } + return false; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDeviceImp::setFormat(const TSoundTrackFormat &fmt) +{ + int bps, ch, status; + TUINT32 sampleRate; + + ch = fmt.m_channelCount; + sampleRate = fmt.m_sampleRate; + + if (m_dev == -1) + if (!doOpenDevice()) + return; + + if (fmt.m_bitPerSample == 8) { + if (fmt.m_signedSample) + bps = AFMT_S8; + else + bps = AFMT_U8; + } else if (fmt.m_bitPerSample == 16) { + bps = AFMT_S16_NE; + } + int bitPerSample = bps; + + status = ioctl(m_dev, SNDCTL_DSP_SETFMT, &bps); + if (status == -1) { + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of bits"); + } + + status = ioctl(m_dev, SNDCTL_DSP_CHANNELS, &ch); + if (status == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of channel"); + + if (ioctl(m_dev, SNDCTL_DSP_SPEED, &sampleRate) == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified sample rate"); + + if (ch != fmt.m_channelCount || bps != bitPerSample || sampleRate != fmt.m_sampleRate) { + doCloseDevice(); + m_currentFormat = TSoundTrackFormat(); + return; + } + m_currentFormat = fmt; +} + +//============================================================================== + +class TPlayTask : public TThread::Runnable +{ + SmartWatch *m_stopWatch; + +public: + TSoundOutputDeviceImp *m_devImp; + TSoundTrackP m_sndtrack; + static int m_skipBytes; + + TPlayTask(TSoundOutputDeviceImp *devImp, const TSoundTrackP &st); + + ~TPlayTask() { delete m_stopWatch; } + + void run(); + void run2(); +}; + +//------------------------------------------------------------------------------- + +TPlayTask::TPlayTask(TSoundOutputDeviceImp *devImp, const TSoundTrackP &st) + : Runnable(), m_stopWatch(new SmartWatch), m_devImp(devImp), m_sndtrack(st) +{ + if (st->getFormat() != m_devImp->m_currentFormat) + if (m_devImp->doCloseDevice()) + m_devImp->setFormat(st->getFormat()); + m_stopWatch->start(); +}; + +//------------------------------------------------------------------------------- + +void TPlayTask::run() +{ + int bytesLeft = m_sndtrack->getSampleCount() * m_sndtrack->getSampleSize(); + char *buf = (char *)m_sndtrack->getRawData(); + int done = 0; + int written = 0; + TINT32 sampleSize = (TINT32)m_sndtrack->getSampleSize(); + const double msToBytes = sampleSize * m_sndtrack->getSampleRate() / 1000.; + + TThread::milestone(); + double startupDelay = m_stopWatch->getTotalTime(); + //std::cout << "ritardo iniziale " << startupDelay << std::endl; + int miss = 0; + m_stopWatch->start(); //e' meglio ignorare il ritardo iniziale + if (done > 0) { + m_stopWatch->addDelay(((done / sampleSize) * 1000) / double(m_sndtrack->getSampleRate())); + } + int auxbuffersize = 0; + int preWrittenBytes = 0; + int bytesToSkipNext; + TSoundTrackP src = TSoundTrack::create(m_sndtrack->getFormat(), 1); + TSoundTrackP dst = src; + TSoundTrackP newSrc = src; + try { + do //gia' tracce accodate + { + bool changeSnd = false; + do //c'e' il flag loop settato + { + while ((bytesLeft > 0)) { + TThread::milestone(); + changeSnd = false; + audio_buf_info info; + TINT32 bytesToWrite = 0; + TINT32 bytesToWriteNext = 0; + double samplesDone = done / (double)sampleSize; + double trackTime = (samplesDone * 1000.) / m_sndtrack->getSampleRate(); + double curTime = m_stopWatch->getTotalTime(); + double delta = curTime - trackTime; + + /* + delta + == 0 sync + < 0 audio piu' veloce del tempo di playback --> simuliamo un ritardo con un continue; + > 0 audio piu' lento del playback -> skip una porzione di audio + */ + + const double minDelay = -10; + const double maxDelay = 0; + //if (delta>maxDelay) + //std::cout << "buffer underrun:" << delta << std::endl; + //std::cout << "buffer " << (deltamaxDelay?"underrun":"sync") << " " << delta<< std::endl; + + if (delta < minDelay) //overrun + { + //std::cout << "out of sync -> audio troppo veloce" << std::endl; + continue; + } + + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_GETOSPACE, &info) == -1) { + miss++; + break; + } + + int fragmentsFree_bytes = info.fragsize * info.fragments; + if (fragmentsFree_bytes == 0) { + //std::cout << "no bytes left on device" << std::endl; + continue; + } + + int bytesToSkip = 0; + bytesToSkipNext = 0; + if (delta > maxDelay) //underrun + { + //std::cout << "out of sync -> audio troppo lento"< la corrente traccia non basta per avere il sync + bytesLeft -= bytesToSkip; + done += bytesToSkip; + + bytesToWrite = tmin(bytesLeft, fragmentsFree_bytes); + bytesToWriteNext = fragmentsFree_bytes - bytesToWrite; + assert(bytesToWrite >= 0); + assert(bytesToWriteNext >= 0); + + if (bytesToWrite % info.fragsize != 0) { //cerco di gestire il write di un frammento non intero + auxbuffersize = ((bytesToWrite / info.fragsize) + 1) * info.fragsize; + } else + auxbuffersize = 0; + + //--------------write + if (bytesToSkipNext == 0) //la corrente traccia basta per lo skip + { + std::cout << " QUI 0 " << std::endl; + dst = m_sndtrack->extract(done / sampleSize, (done + bytesToWrite) / sampleSize); + if (bytesToSkip != 0) { + //costruisco traccia su cui fare il crossfade + //utilizzo il contenuto della traccia di crossfade + dst = TSop::crossFade(0.2, src, dst); + } + char *auxbuf = new char[fragmentsFree_bytes]; + memcpy(auxbuf, (char *)dst->getRawData(), bytesToWrite); + if (bytesToWriteNext != 0) { + int offset = bytesToWrite; + if (m_devImp->m_looped) { + offset += bytesToWriteNext; + preWrittenBytes = bytesToWriteNext; + memcpy(auxbuf + offset, buf, preWrittenBytes); + newSrc = m_sndtrack->extract(preWrittenBytes / sampleSize, preWrittenBytes / sampleSize); + std::cout << " QUI 1" << std::endl; + } else { + while (!m_devImp->m_waitingTracks.empty()) { + TSoundTrackP st = m_devImp->m_waitingTracks[0].first; + int count = st->getSampleCount() * sampleSize; + if (bytesToWriteNext >= count) { + char *buffer = (char *)st->getRawData(); + memcpy(auxbuf + offset, buffer, count); + bytesToWriteNext -= count; + offset += count; + std::cout << " QUI 2" << std::endl; + if (m_devImp->m_waitingTracks[0].second) { + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract(count / sampleSize, count / sampleSize); + m_sndtrack = st; + preWrittenBytes = 0; + std::cout << " QUI 3" << std::endl; + break; + } + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract(count / sampleSize, count / sampleSize); + } else { + m_sndtrack = st; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + preWrittenBytes = bytesToWriteNext; + buf = (char *)m_sndtrack->getRawData(); + memcpy(auxbuf + offset, buf, bytesToWriteNext); + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract((bytesToWriteNext) / sampleSize, (bytesToWriteNext) / sampleSize); + std::cout << " QUI 4" << std::endl; + break; + } + } //end while + } + + if (fragmentsFree_bytes > offset) { + std::cout << " QUI 5" << std::endl; + int val = m_sndtrack->isSampleSigned() ? 0 : 127; + memset(auxbuf + offset, val, fragmentsFree_bytes - offset); // ci metto silenzio + newSrc = TSoundTrack::create(m_sndtrack->getFormat(), 1); + } + } + + written = write(m_devImp->m_dev, auxbuf, fragmentsFree_bytes); + delete[] auxbuf; + } else //devo skippare anche parte di una delle seguenti + { + std::cout << " QUI 6a" << std::endl; + assert(bytesToWriteNext > 0); + assert(bytesToWriteNext == fragmentsFree_bytes - bytesToWrite); + assert(bytesToWrite == 0); + assert(bytesToSkip != 0); + char *auxbuf = new char[fragmentsFree_bytes]; + //memcpy(auxbuf, buf+done, bytesToWrite); + //TSoundTrackP subCross = m_sndtrack->extract((done+bytesToWrite)/sampleSize, (done+bytesToWrite)/sampleSize); + //togli quelle da skippare + int backupSkipNext = bytesToSkipNext; + while (!m_devImp->m_waitingTracks.empty()) { + TSoundTrackP st = m_devImp->m_waitingTracks[0].first; + int count = st->getSampleCount() * sampleSize; + if (bytesToSkipNext >= count) { + std::cout << " QUI 6b" << std::endl; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + bytesToSkipNext -= count; + } else { + std::cout << " QUI 7" << std::endl; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_sndtrack = st; + buf = (char *)st->getRawData(); + break; + } + } + //scrivi byteWriteNext fai crossfade e cerca quella successiva + //con cui riempire + TINT32 displacement = 0; //deve essere in munero di campioni non bytes + dst = TSoundTrack::create(m_sndtrack->getFormat(), (fragmentsFree_bytes - bytesToWrite) / sampleSize); + int count = m_sndtrack->getSampleCount() * sampleSize; + if (count >= bytesToSkipNext + bytesToWriteNext) //la traccia trovata e' suff sia per skippare che per scrivere + { + preWrittenBytes = bytesToSkipNext + bytesToWriteNext; + dst = m_sndtrack->extract(bytesToSkipNext / sampleSize, preWrittenBytes / sampleSize); + newSrc = m_sndtrack->extract(preWrittenBytes / sampleSize, preWrittenBytes / sampleSize); + } else //non e' suff per scrivere + { + dst->copy(m_sndtrack->extract(bytesToSkipNext / sampleSize, m_sndtrack->getSampleCount() - 1), 0); + displacement = m_sndtrack->getSampleCount() - bytesToSkipNext / sampleSize; + bytesToWriteNext -= displacement * sampleSize; + while (!m_devImp->m_waitingTracks.empty()) { + TSoundTrackP st = m_devImp->m_waitingTracks[0].first; + int count = st->getSampleCount() * sampleSize; + if (bytesToWriteNext >= count) { + std::cout << " QUI 8" << std::endl; + dst->copy(st, displacement); + bytesToWriteNext -= count; + displacement += count; + if (m_devImp->m_waitingTracks[0].second) { + std::cout << " QUI 9" << std::endl; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract(count / sampleSize, count / sampleSize); + m_sndtrack = st; + break; + } + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract(count / sampleSize, count / sampleSize); + } else { + std::cout << " QUI 10" << std::endl; + dst->copy(st->extract(0L, bytesToWriteNext / sampleSize), displacement); + m_sndtrack = st; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + preWrittenBytes = bytesToWriteNext; + done = preWrittenBytes; + bytesLeft = m_sndtrack->getSampleCount() * sampleSize - done; + buf = (char *)m_sndtrack->getRawData(); + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + newSrc = m_sndtrack->extract(preWrittenBytes / sampleSize, preWrittenBytes / sampleSize); + break; + } + } + bytesToSkipNext = backupSkipNext; + } + + TSoundTrackP st = TSop::crossFade(0.2, src, dst); + memcpy(auxbuf + bytesToWrite, (char *)st->getRawData(), fragmentsFree_bytes - bytesToWrite); + + //devo ricercare quella giusta che non deve essere skippata + //ma sostitutita come traccia corrente + //devo fare un cross fade + written = write(m_devImp->m_dev, auxbuf, fragmentsFree_bytes); + delete[] auxbuf; + } + //----------- end write + src = newSrc; + if (written == -1) + break; + std::cout << written << " " << (bytesToWrite + preWrittenBytes) << std::endl; + if (written != bytesToWrite + preWrittenBytes) + break; + std::cout << " update done 2" << std::endl; + bytesLeft -= written; + done += written; + } //chiudo il while((bytesLeft > 0)) + std::cout << " QUI 11" << std::endl; + done = preWrittenBytes + bytesToSkipNext; + written = 0; + bytesLeft = m_sndtrack->getSampleCount() * sampleSize - done; + m_stopWatch->start(); + if (done > 0) { + m_stopWatch->addDelay(((done / m_sndtrack->getSampleSize()) * 1000) / double(m_sndtrack->getSampleRate())); + } + preWrittenBytes = 0; + } while (m_devImp->m_looped || changeSnd); + + if (m_devImp->m_waitingTracks.empty()) + break; + m_sndtrack = m_devImp->m_waitingTracks[0].first; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + bytesLeft = m_sndtrack->getSampleCount() * m_sndtrack->getSampleSize(); + buf = (char *)m_sndtrack->getRawData(); + done = 0; + written = 0; + + m_stopWatch->start(); //ignoro il ritardo iniziale + if (done > 0) { + m_stopWatch->addDelay(((done / m_sndtrack->getSampleSize()) * 1000) / double(m_sndtrack->getSampleRate())); + } + } while (true); //ci sono gia' tracce accodate + + if (!m_devImp->m_waitingTracks.empty()) { + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_executor.addTask(new TPlayTask(m_devImp, m_devImp->m_waitingTracks[0].first)); + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + //std::cout<<"OPS ..... erase 4"<m_dev != -1) { + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_SYNC) == -1) { + std::cout << "unable to sync! " << std::endl; + throw TException("unable to sync!"); + } + + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + + //std::cout << "miss = " << miss << std::endl; + } + } catch (TThread::Interrupt &e) { + std::cout << "Play interrupted " << e.getMessage() << std::endl; + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + } catch (TException &e) { + std::cout << "esco dal play " << e.getMessage() << std::endl; + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + } +} + +//------------------------------------------------------------------------------- + +void TPlayTask::run2() +{ + int bytesLeft = m_sndtrack->getSampleCount() * m_sndtrack->getSampleSize(); + char *buf = (char *)m_sndtrack->getRawData(); + int done = 0; + int written = 0; + TINT32 sampleSize = (TINT32)m_sndtrack->getSampleSize(); + const double msToBytes = sampleSize * m_sndtrack->getSampleRate() / 1000.; + + TThread::milestone(); + double startupDelay = m_stopWatch->getTotalTime(); + //std::cout << "ritardo iniziale " << startupDelay << std::endl; + int miss = 0; + m_stopWatch->start(); //e' meglio ignorare il ritardo iniziale + if (done > 0) { + m_stopWatch->addDelay(((done / sampleSize) * 1000) / double(m_sndtrack->getSampleRate())); + } + int auxbuffersize = 0; + int preWrittenBytes = 0; + TSoundTrackP src = TSoundTrack::create(m_sndtrack->getFormat(), 1); + TSoundTrackP dst = src; + try { + do //gia' tracce accodate + { + bool changeSnd = false; + do //c'e' il flag loop settato + { + while ((bytesLeft > 0)) { + changeSnd = false; + TThread::milestone(); + audio_buf_info info; + TINT32 bytesToWrite; + double samplesDone = done / (double)sampleSize; + double trackTime = (samplesDone * 1000.) / m_sndtrack->getSampleRate(); + double curTime = m_stopWatch->getTotalTime(); + double delta = curTime - trackTime; + + /* + delta + == 0 sync + < 0 audio piu' veloce del tempo di playback --> simuliamo un ritardo con un continue; + > 0 audio piu' lento del playback -> skip una porzione di audio + */ + + const double minDelay = -10; + const double maxDelay = 0; + //if (delta>maxDelay) + //std::cout << "buffer underrun:" << delta << std::endl; + //std::cout << "buffer " << (deltamaxDelay?"underrun":"sync") << " " << delta<< std::endl; + + if (delta < minDelay) //overrun + { + //std::cout << "out of sync -> audio troppo veloce" << std::endl; + continue; + } + + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_GETOSPACE, &info) == -1) { + miss++; + break; + } + + int fragmentsFree_bytes = info.fragsize * info.fragments; + if (fragmentsFree_bytes == 0) { + //std::cout << "no bytes left on device" << std::endl; + continue; + } + + int bytesToSkip = 0; + int bigSkip = 0; + if (delta > maxDelay) //underrun + { + //std::cout << "out of sync -> audio troppo lento"<m_waitingTracks.empty()) { + TSoundTrackP st = m_devImp->m_waitingTracks[0].first; + int count = st->getSampleCount() * sampleSize; + if (bigSkip >= count) { + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + bigSkip -= count; + } else + break; + } + + preWrittenBytes = 0; + if (auxbuffersize == 0) { + if (bytesToSkip != 0) { + //costruisco traccia su cui fare il crossfade + //utilizzo il contenuto della traccia di crossfade + dst = m_sndtrack->extract(done / sampleSize, (done + bytesToWrite) / sampleSize); + TSoundTrackP st = TSop::crossFade(0.2, src, dst); + char *buffer = (char *)st->getRawData(); + written = write(m_devImp->m_dev, buffer, bytesToWrite); + } else + written = write(m_devImp->m_dev, buf + done, bytesToWrite); + src = m_sndtrack->extract((done + bytesToWrite) / sampleSize, (done + bytesToWrite) / sampleSize); + } else { //auxbuffersize != 0 sse il numero di bytes residui nella traccia e' inferiore alla dimensione del frammento + char *auxbuf = new char[auxbuffersize]; + TSoundTrackP newSrc; + dst = TSoundTrack::create(m_sndtrack->getFormat(), auxbuffersize / sampleSize); + memcpy(auxbuf, buf + done, bytesToWrite); + dst->copy(m_sndtrack->extract(done / sampleSize, (done + bytesToWrite) / sampleSize), 0); + preWrittenBytes = auxbuffersize - bytesToWrite; + if (m_devImp->m_looped) { + memcpy(auxbuf + bytesToWrite, buf, preWrittenBytes); + dst->copy(m_sndtrack->extract(0, preWrittenBytes / sampleSize), bytesToWrite / sampleSize); + newSrc = m_sndtrack->extract(preWrittenBytes / sampleSize, preWrittenBytes / sampleSize); + } else { + newSrc = TSoundTrack::create(m_sndtrack->getFormat(), 1); + static int added = 0; + //se non c'e' alcuna altra traccia o e di diverso format + //riempo il frammento con del silenzio + if (m_devImp->m_waitingTracks.empty() || + (m_sndtrack->getFormat() != m_devImp->m_waitingTracks[0].first->getFormat())) { + int val = m_sndtrack->isSampleSigned() ? 0 : 127; + memset(auxbuf + bytesToWrite, val, preWrittenBytes); // ci metto silenzio + } else + while (true) //ci sono altre tracce accodate + { + TSoundTrackP st = m_devImp->m_waitingTracks[0].first; + int sampleBytes = st->getSampleCount() * st->getSampleSize(); + if (sampleBytes >= preWrittenBytes - added) { + //La traccia ha abbastanza campioni per riempire il frammento + //quindi la sostituisco alla corrente del runnable e continuo + buf = (char *)st->getRawData(); + memcpy(auxbuf + bytesToWrite, buf, preWrittenBytes - added); + m_sndtrack = st; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + changeSnd = true; + dst->copy(m_sndtrack->extract(0, (preWrittenBytes - added) / sampleSize), bytesToWrite / sampleSize + added); + newSrc = m_sndtrack->extract((preWrittenBytes - added) / sampleSize, (preWrittenBytes - added) / sampleSize); + break; + } else { //occhio al loop + //La traccia successiva e' piu corta del frammento da riempire quindi + //ce la metto tutta e se non ha il flag di loop settato cerco di aggiungere + //i byte della successiva + memcpy(auxbuf + bytesToWrite, st->getRawData(), sampleBytes); + dst->copy(st->extract(0, st->getSampleCount() - 1), bytesToWrite / sampleSize); + added += st->getSampleCount(); + if (m_devImp->m_waitingTracks[0].second) //e' quella che deve essere in loop + { + buf = (char *)st->getRawData(); + m_sndtrack = st; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + preWrittenBytes = 0; + bytesLeft = 0; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + changeSnd = true; + break; + } + + //la elimino e vedo se esiste la successiva altrimenti metto campioni "zero" + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + if (!m_devImp->m_waitingTracks.empty()) { + st = m_devImp->m_waitingTracks[0].first; + std::cout << " Traccia con meno campioni cerco la successiva" << std::endl; + } else { + int val = m_sndtrack->isSampleSigned() ? 0 : 127; + memset(auxbuf + bytesToWrite, val, preWrittenBytes - sampleBytes); // ci metto silenzio + std::cout << "OPS ..... silence" << std::endl; + break; + } + } + } //end while(true) + } + //qui andrebbe fatto un cross-fade se c'erano da skippare campioni => bytesToSkip != 0 + if (bytesToSkip != 0) { + TSoundTrackP st = TSop::crossFade(0.2, src, dst); + char *buffer = (char *)st->getRawData(); + written = write(m_devImp->m_dev, buffer, bytesToWrite); + } else + written = write(m_devImp->m_dev, auxbuf, auxbuffersize); + src = newSrc; + auxbuffersize = 0; + delete[] auxbuf; + } + if (written == -1) + break; + if (written != bytesToWrite + preWrittenBytes) + break; + bytesLeft -= written; + done += written; + } //chiudo il while((bytesLeft > 0)) + done = preWrittenBytes; + written = 0; + bytesLeft = m_sndtrack->getSampleCount() * m_sndtrack->getSampleSize() - done; + m_stopWatch->start(); + if (done > 0) { + m_stopWatch->addDelay(((done / m_sndtrack->getSampleSize()) * 1000) / double(m_sndtrack->getSampleRate())); + } + } while (m_devImp->m_looped || changeSnd); + + if (m_devImp->m_waitingTracks.empty()) { + //std::cout<<"OPS ..... non accodato"<m_waitingTracks[0].first; + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + bytesLeft = m_sndtrack->getSampleCount() * m_sndtrack->getSampleSize(); + buf = (char *)m_sndtrack->getRawData(); + done = 0; + written = 0; + + m_stopWatch->start(); //ignoro il ritardo iniziale + if (done > 0) { + m_stopWatch->addDelay(((done / m_sndtrack->getSampleSize()) * 1000) / double(m_sndtrack->getSampleRate())); + } + } while (true); //ci sono gia' tracce accodate + + if (!m_devImp->m_waitingTracks.empty()) { + m_devImp->m_looped = m_devImp->m_waitingTracks[0].second; + m_devImp->m_executor.addTask(new TPlayTask(m_devImp, m_devImp->m_waitingTracks[0].first)); + m_devImp->m_waitingTracks.erase(m_devImp->m_waitingTracks.begin()); + //std::cout<<"OPS ..... erase 4"<m_dev != -1) { + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_SYNC) == -1) { + std::cout << "unable to sync! " << std::endl; + throw TException("unable to sync!"); + } + + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + + //std::cout << "miss = " << miss << std::endl; + } + } catch (TThread::Interrupt &e) { + std::cout << "Play interrupted " << e.getMessage() << std::endl; + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + } catch (TException &e) { + std::cout << "esco dal play " << e.getMessage() << std::endl; + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + } +} + +//============================================================================== + +TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp) +{ + if (m_imp->doOpenDevice()) { + m_imp->insertAllRate(); + try { + if (!m_imp->verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "No default samplerate are supported"); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + m_imp->doCloseDevice(); + } +} + +//------------------------------------------------------------------------------ + +TSoundOutputDevice::~TSoundOutputDevice() +{ + close(); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::installed() +{ + bool ret = false; + int dev = ::open("/dev/dsp", O_WRONLY, 0); + if (dev >= 0) { + ret = true; + ::close(dev); + } + return ret; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::open(const TSoundTrackP &st) +{ + m_imp->m_currentFormat = st->getFormat(); + try { + m_imp->doOpenDevice(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::close() +{ + stop(); + if (m_imp->m_dev != -1) { + bool closed = m_imp->doCloseDevice(); + if (!closed) + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, + "Error during the closing of the output device"); + } + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::attach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.insert(listener); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::detach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.erase(listener); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) +{ + assert((scrubbing && !loop) || !scrubbing); + if (!st->getSampleCount()) + return; + + TThread::ScopedLock sl(m_imp->m_mutex); + if (m_imp->m_looped) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Unable to queue another playback when the sound player is looping"); + + TSoundTrackFormat format = st->getFormat(); + if (!m_imp->isSupportFormat(format)) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported format for playback"); + } + + if (m_imp->m_isPlaying) { + assert(s1 >= s0); + TSoundTrackP subTrack = st->extract(s0, s1); + m_imp->m_waitingTracks.push_back(std::make_pair(subTrack, loop)); + //std::cout<<"Sono in pushback"<m_dev == -1)) + try { + if (m_imp->doOpenDevice()) + m_imp->setFormat(format); + } catch (TSoundDeviceException &e) { + m_imp->doCloseDevice(); + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + m_imp->m_isPlaying = true; + m_imp->m_stopped = false; + m_imp->m_looped = loop; + //m_imp->m_currentFormat = st->getFormat(); + + assert(s1 >= s0); + TSoundTrackP subTrack = st->extract(s0, s1); + + m_imp->m_executor.addTask(new TPlayTask(m_imp, subTrack)); +} +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::stop() +{ + TThread::ScopedLock sl(m_imp->m_mutex); + if (!m_imp->m_isPlaying) + return; + m_imp->m_executor.cancel(); + ioctl(m_imp->m_dev, SNDCTL_DSP_POST, 0); + m_imp->m_isPlaying = false; + m_imp->m_stopped = true; + m_imp->m_looped = false; + m_imp->m_waitingTracks.clear(); +} + +//------------------------------------------------------------------------------ +double TSoundOutputDevice::getVolume() +{ + int mixer; + if ((mixer = openMixer()) < 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't open the mixer device"); + + int devmask; + if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int recmask; + if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &recmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int stereo; + if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int outmask = devmask | ~recmask; + + int index; + if (outmask & (1 << SOUND_MIXER_ALTPCM)) + index = SOUND_MIXER_ALTPCM; + else if (outmask & (1 << SOUND_MIXER_PCM)) + index = SOUND_MIXER_PCM; + + int level; + if (ioctl(mixer, MIXER_READ(index), &level) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to read the volume"); + } + if ((1 << index) & stereo) { + int left = level & 0xff; + int right = ((level & 0xff00) >> 8); + ::close(mixer); + return (left + right) / 20.0; + } else { + ::close(mixer); + return (level & 0xff) / 10.0; + } +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::setVolume(double volume) +{ + int mixer; + if ((mixer = openMixer()) < 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't open the mixer device"); + + int devmask; + if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int recmask; + if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &recmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int stereo; + if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int outmask = devmask | ~recmask; + + if (outmask & (1 << SOUND_MIXER_ALTPCM)) { + int vol, index = SOUND_MIXER_ALTPCM; + if ((1 << index) & stereo) { + volume *= 10.0; + vol = (int)volume + ((int)(volume * 256.0)); + } else + vol = (int)(volume * 10.0); + + if (!writeVolume(vol, mixer, index)) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Can't write the volume"); + } + + // metto anche l'altro ad un livello di sensibilita' adeguata + if (outmask & (1 << SOUND_MIXER_PCM)) { + int vol, index = SOUND_MIXER_PCM; + double volDefault = 6.7; + if ((1 << index) & stereo) { + volDefault *= 10.0; + vol = (int)volDefault + ((int)(volDefault * 256.0)); + } else + vol = (int)(volDefault * 10.0); + + if (!writeVolume(vol, mixer, index)) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Can't write the volume"); + } + } + } else if (outmask & (1 << SOUND_MIXER_PCM)) { + int vol, index = SOUND_MIXER_PCM; + if ((1 << index) & stereo) { + volume *= 10.0; + vol = (int)volume + ((int)(volume * 256.0)); + } else + vol = (int)(volume * 10.0); + + if (!writeVolume(vol, mixer, index)) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't write the volume"); + } + } + + ::close(mixer); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isPlaying() const +{ + TThread::ScopedLock sl(m_imp->m_mutex); + return m_imp->m_isPlaying; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isLooping() +{ + TThread::ScopedLock sl(m_imp->m_mutex); + return m_imp->m_looped; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::setLooping(bool loop) +{ + TThread::ScopedLock sl(m_imp->m_mutex); + m_imp->m_looped = loop; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::supportsVolume() +{ + int mixer; + if ((mixer = openMixer()) < 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't open the mixer device"); + + int devmask; + if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int recmask; + if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &recmask) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int stereo; + if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Error in ioctl with mixer device"); + } + + int outmask = devmask | ~recmask; + + if ((outmask & (1 << SOUND_MIXER_ALTPCM)) || (outmask & (1 << SOUND_MIXER_PCM))) { + ::close(mixer); + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + if (bitPerSample == 8) + fmt = TSoundTrackFormat(sampleRate, channelCount, bitPerSample, false); + else + fmt = TSoundTrackFormat(sampleRate, channelCount, bitPerSample); + if (m_imp->isSupportFormat(fmt)) + return fmt; + + int bps, ch, status; + + bps = bitPerSample; + ch = channelCount; + + if (m_imp->m_dev == -1) + m_imp->doOpenDevice(); + + if (bitPerSample <= 8) { + bitPerSample = AFMT_U8; + fmt.m_signedSample = false; + } else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) { + bitPerSample = AFMT_S16_NE; + fmt.m_signedSample = true; + } + + status = ioctl(m_imp->m_dev, SNDCTL_DSP_SETFMT, &bitPerSample); + if (status == -1) { + perror("CHE palle "); + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of bits"); + } + fmt.m_bitPerSample = bitPerSample; + + status = ioctl(m_imp->m_dev, SNDCTL_DSP_CHANNELS, &channelCount); + if (status == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of channel"); + fmt.m_channelCount = channelCount; + + if (m_imp->m_supportedRate.find((int)sampleRate) == m_imp->m_supportedRate.end()) { + std::set::iterator it = m_imp->m_supportedRate.lower_bound((int)sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported rate"); + } else + sampleRate = *it; + } + + if (ioctl(m_imp->m_dev, SNDCTL_DSP_SPEED, &sampleRate) == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified sample rate"); + fmt.m_sampleRate = sampleRate; + + if (ch != channelCount || bps != bitPerSample) { + m_imp->doCloseDevice(); + } + + return fmt; +} + +//------------------------------------------------------------------------------ +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( + const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } +} + +//====================================================================== +//====================================================================== +// CLASSI PER IL RECORD +//====================================================================== +//====================================================================== + +class TSoundInputDeviceImp +{ +public: + int m_dev; + bool m_stopped; + bool m_isRecording; + TSoundTrackFormat m_currentFormat; + TSoundTrackP m_st; + std::set m_supportedRate; + + TINT32 m_recordedSampleCount; + vector m_recordedBlocks; + vector m_samplePerBlocks; + bool m_oneShotRecording; + + TThread::Executor m_executor; + + TSoundInputDeviceImp() + : m_dev(-1), m_stopped(false), m_isRecording(false), m_st(0), m_supportedRate(), m_recordedBlocks(), m_samplePerBlocks(), m_oneShotRecording(false) + { + } + + ~TSoundInputDeviceImp() {} + + bool doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType); + bool doCloseDevice(); + void insertAllRate(); + bool verifyRate(); +}; + +//------------------------------------------------------------------------------ + +bool TSoundInputDeviceImp::doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType) +{ + m_dev = open("/dev/dsp", O_RDONLY); + if (m_dev < 0) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Cannot open the dsp device"); + + //ioctl(m_dev, SNDCTL_DSP_RESET,0); + + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDeviceImp::doCloseDevice() +{ + if (close(m_dev) < 0) + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, + "Cannot close the dsp device"); + m_dev = -1; + return true; +} + +//---------------------------------------------------------------------------- + +void TSoundInputDeviceImp::insertAllRate() +{ + m_supportedRate.insert(8000); + m_supportedRate.insert(11025); + m_supportedRate.insert(16000); + m_supportedRate.insert(22050); + m_supportedRate.insert(32000); + m_supportedRate.insert(44100); + m_supportedRate.insert(48000); +} + +//---------------------------------------------------------------------------- + +bool TSoundInputDeviceImp::verifyRate() +{ + std::set::iterator it; + + for (it = m_supportedRate.begin(); + it != m_supportedRate.end(); ++it) { + int sampleRate = *it; + if (ioctl(m_dev, SNDCTL_DSP_SPEED, &sampleRate) == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified sample rate"); + if (sampleRate != *it) + m_supportedRate.erase(*it); + } + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + + return true; +} +//============================================================================== + +class TRecordTask : public TThread::Runnable +{ +public: + TSoundInputDeviceImp *m_devImp; + + TRecordTask(TSoundInputDeviceImp *devImp) + : Runnable(), m_devImp(devImp){}; + + ~TRecordTask(){}; + + void run(); +}; + +//------------------------------------------------------------------------------ + +void TRecordTask::run() +{ + // N.B. e' bene che la dimensione sia piccola cosi' l'attesa perche' termini + // e' minore in questo caso vogliamo 16 frammenti ognuno di 4096 byte + int fraginfo = (16 << 16) | 12; + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_SETFRAGMENT, &fraginfo) == -1) + perror("SETFRAGMENT"); + + int fragsize = 0; + if (ioctl(m_devImp->m_dev, SNDCTL_DSP_GETBLKSIZE, &fragsize) == -1) + perror("GETFRAGMENT"); + TINT32 byteRecordedSample = 0; + + if (m_devImp->m_oneShotRecording) { + TINT32 byteToSample = m_devImp->m_st->getSampleSize() * m_devImp->m_st->getSampleCount(); + char *buf = (char *)m_devImp->m_st->getRawData(); + + while ((byteRecordedSample < byteToSample) && m_devImp->m_isRecording) { + int sample; + if (fragsize > (byteToSample - byteRecordedSample)) + sample = byteToSample - byteRecordedSample; + else + sample = fragsize; + int nread = read(m_devImp->m_dev, buf + byteRecordedSample, sample); + if (nread == -1) + break; + if (nread != sample) + break; + + byteRecordedSample += nread; + } + } else { + int bytePerSample = m_devImp->m_currentFormat.m_bitPerSample >> 3; + switch (bytePerSample) { + case 3: + bytePerSample++; + break; + default: + break; + } + bytePerSample *= m_devImp->m_currentFormat.m_channelCount; + + while (m_devImp->m_isRecording) { + char *dataBuffer = new char[fragsize]; + m_devImp->m_recordedBlocks.push_back(dataBuffer); + m_devImp->m_samplePerBlocks.push_back(fragsize); + + int nread = read(m_devImp->m_dev, dataBuffer, fragsize); + if (nread == -1) + break; + if (nread != fragsize) + break; + + m_devImp->m_recordedSampleCount += (fragsize / bytePerSample); + } + } + ioctl(m_devImp->m_dev, SNDCTL_DSP_RESET, 0); +} + +//============================================================================== + +TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp) +{ + m_imp->m_dev = open("/dev/dsp", O_RDONLY); + if (m_imp->m_dev < 0) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Cannot open the dsp device"); + + m_imp->insertAllRate(); + try { + if (!m_imp->verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "No default samplerate are supported"); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + m_imp->doCloseDevice(); +} + +//------------------------------------------------------------------------------ + +TSoundInputDevice::~TSoundInputDevice() +{ + if (m_imp->m_dev != -1) + m_imp->doCloseDevice(); + delete m_imp; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::installed() +{ + bool ret = false; + int dev = ::open("/dev/dsp", O_RDONLY); + if (dev >= 0) { + ret = true; + ::close(dev); + } + return ret; +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackFormat &format, TSoundInputDevice::Source type) +{ + m_imp->m_recordedBlocks.clear(); + m_imp->m_samplePerBlocks.clear(); + + //registra creando una nuova traccia + m_imp->m_oneShotRecording = false; + try { + if (m_imp->m_dev == -1) + m_imp->doOpenDevice(format, type); + + if (!selectInputDevice(type)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Input device is not supported for recording"); + + TSoundTrackFormat fmt = getPreferredFormat(format); + if (fmt != format) + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported format"); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + try { + if (getVolume() == 0.0) { + double volume = 5.0; + setVolume(volume); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + m_imp->m_currentFormat = format; + m_imp->m_isRecording = true; + m_imp->m_stopped = false; + m_imp->m_recordedSampleCount = 0; + + //far partire il thread + /*TRecordThread *recordThread = new TRecordThread(m_imp); + if (!recordThread) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Problemi per creare il thread"); + recordThread->start();*/ + m_imp->m_executor.addTask(new TRecordTask(m_imp)); +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackP &st, TSoundInputDevice::Source type) +{ + m_imp->m_recordedBlocks.clear(); + m_imp->m_samplePerBlocks.clear(); + + try { + if (m_imp->m_dev == -1) + m_imp->doOpenDevice(st->getFormat(), type); + + if (!selectInputDevice(type)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Input device is not supported for recording"); + + TSoundTrackFormat fmt = getPreferredFormat(st->getFormat()); + if (fmt != st->getFormat()) + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported format"); + + if (getVolume() == 0.0) { + double volume = 5.0; + setVolume(volume); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + //Sovrascive un'intera o parte di traccia gia' esistente + m_imp->m_oneShotRecording = true; + m_imp->m_currentFormat = st->getFormat(); + m_imp->m_isRecording = true; + m_imp->m_stopped = false; + m_imp->m_recordedSampleCount = 0; + m_imp->m_st = st; + + m_imp->m_recordedBlocks.push_back((char *)st->getRawData()); + + //far partire il thread + /*TRecordThread *recordThread = new TRecordThread(m_imp); + if (!recordThread) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Problemi per creare il thread"); + recordThread->start();*/ + m_imp->m_executor.addTask(new TRecordTask(m_imp)); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundInputDevice::stop() +{ + TSoundTrackP st; + + if (!m_imp->m_isRecording) + return st; + + m_imp->m_isRecording = false; + + // mettere istruzioni per fermare il rec + ioctl(m_imp->m_dev, SNDCTL_DSP_SYNC, 0); + try { + m_imp->doCloseDevice(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + // attendo 1/5 di secondo + usleep(200000); + if (m_imp->m_oneShotRecording) + st = m_imp->m_st; + else { + st = TSoundTrack::create(m_imp->m_currentFormat, m_imp->m_recordedSampleCount); + TINT32 bytesCopied = 0; + + for (int i = 0; i < (int)m_imp->m_recordedBlocks.size(); ++i) { + memcpy( + (void *)(st->getRawData() + bytesCopied), + m_imp->m_recordedBlocks[i], + m_imp->m_samplePerBlocks[i]); + delete[] m_imp->m_recordedBlocks[i]; + + bytesCopied += m_imp->m_samplePerBlocks[i]; + } + m_imp->m_samplePerBlocks.clear(); + } + + return st; +} + +//------------------------------------------------------------------------------ + +double TSoundInputDevice::getVolume() +{ + int mixer; + if ((mixer = openMixer()) < 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't have the mixer"); + + int index; + if ((index = getCurrentRecordSource(mixer)) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + + int stereo; + if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + + int level; + if (ioctl(mixer, MIXER_READ(index), &level) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Can't read the volume value"); + } + + if ((1 << index) & stereo) { + int left = level & 0xff; + int right = ((level & 0xff00) >> 8); + ::close(mixer); + return (left + right) / 20.0; + } else { + ::close(mixer); + return (level & 0xff) / 10.0; + } +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::setVolume(double volume) +{ + int mixer; + if ((mixer = openMixer()) < 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't have the mixer"); + + int caps; + if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + + if (!(caps & SOUND_CAP_EXCL_INPUT)) { + int rec; + if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &rec) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + int i; + int nosound = 0; + for (i = 0; i < 32; ++i) + if (rec & (1 << i)) + if (!writeVolume(nosound, mixer, i)) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Can't set the volume value"); + } + } + + int index; + if ((index = getCurrentRecordSource(mixer)) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + + int stereo; + if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Can't obtain information by mixer"); + } + + int vol; + if ((1 << index) & stereo) { + volume *= 10.0; + vol = (int)volume + ((int)(volume * 256.0)); + } else + vol = (int)(volume * 10.0); + + if (!writeVolume(vol, mixer, index)) { + ::close(mixer); + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Can't write the volume value"); + } + ::close(mixer); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::isRecording() +{ + return m_imp->m_isRecording; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + int status; + + if (bitPerSample <= 8) { + bitPerSample = AFMT_U8; + fmt.m_signedSample = false; + } else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) { + bitPerSample = AFMT_S16_NE; + fmt.m_signedSample = true; + } + + status = ioctl(m_imp->m_dev, SNDCTL_DSP_SETFMT, &bitPerSample); + if (status == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of bits"); + fmt.m_bitPerSample = bitPerSample; + + status = ioctl(m_imp->m_dev, SNDCTL_DSP_CHANNELS, &channelCount); + if (status == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified number of channel"); + fmt.m_channelCount = channelCount; + + if (m_imp->m_supportedRate.find((int)sampleRate) == + m_imp->m_supportedRate.end()) { + std::set::iterator it = + m_imp->m_supportedRate.lower_bound((int)sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported rate"); + } else + sampleRate = *it; + } + + if (ioctl(m_imp->m_dev, SNDCTL_DSP_SPEED, &sampleRate) == -1) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Failed setting the specified sample rate"); + fmt.m_sampleRate = sampleRate; + + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } +} + +//****************************************************************************** +//****************************************************************************** +// funzioni per l'interazione con la libreria OSS +//****************************************************************************** +//****************************************************************************** +namespace +{ +string parseError(int error) +{ + switch (error) { + case EBADF: + return string("Bad file descriptor"); + case EFAULT: + return string("Pointer to/from buffer data is invalid"); + case EINTR: + return string("Signal interrupt the signal"); + case EINVAL: + return string("Request/arg isn't valid for this device"); + case EIO: + return string("Some phisical I/O error has occurred"); + case ENOTTY: + return string("Fieldes isn't associated with a device that accepts control"); + case ENXIO: + return string("Request/arg valid, but the requested cannot be performed on this subdevice"); + default: + return string("Unknown error"); + break; + } +} + +//------------------------------------------------------------------------------ + +TSoundInputDevice::Source stringToSource(string dev) +{ + if (dev == "mic") + return TSoundInputDevice::Mic; + else if (dev == "line") + return TSoundInputDevice::LineIn; + else if (dev == "cd") + return TSoundInputDevice::CdAudio; + else + return TSoundInputDevice::DigitalIn; +} + +//------------------------------------------------------------------------------ + +string sourceToString(TSoundInputDevice::Source dev) +{ + switch (dev) { + case TSoundInputDevice::Mic: + return string("mic"); + case TSoundInputDevice::LineIn: + return string("line"); + case TSoundInputDevice::DigitalIn: + return string("digital"); + default: + return string("cd"); + } +} + +//------------------------------------------------------------------------------ + +int openMixer() +{ + int mixer = open("/dev/mixer", O_RDWR); + if (mixer == -1) + return false; + return mixer; +} +//------------------------------------------------------------------------------ + +int getCurrentRecordSource(int mixer) +{ + int recsrc; + if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) + return -1; + int index = -1; + for (index = 0; index < 32; ++index) + if (recsrc & 1 << index) + break; + return index; +} +//------------------------------------------------------------------------------ + +bool writeVolume(int volume, int mixer, int indexDev) +{ + if (ioctl(mixer, MIXER_WRITE(indexDev), &volume) == -1) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +bool controlEnableRecord(int mixer) +{ + int recmask; + if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &recmask) == -1) { + perror("Read recmask"); + return false; + } + if (recmask & (1 << SOUND_MIXER_IGAIN)) { + int volume; + if (ioctl(mixer, MIXER_READ(SOUND_MIXER_IGAIN), &volume) == -1) + return false; + + int app = (volume & 0xff); + if (app <= 30) { + volume = 80 | 80 << 8; + if (!writeVolume(volume, mixer, SOUND_MIXER_IGAIN)) + return false; + } + } + return true; +} + +//------------------------------------------------------------------------------ + +int isInputDeviceSupported(TSoundInputDevice::Source dev, int &mixer) +{ + int recmask; + if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &recmask) == -1) { + perror("Read recmask"); + return -1; + } + + int i; + string devS = sourceToString(dev); + const char *deviceName[] = SOUND_DEVICE_NAMES; + for (i = 0; i < 32; ++i) { + if (!(recmask & 1 << i)) + continue; + if (strcmp(devS.c_str(), deviceName[i]) == 0) + return i; + } + return -1; +} + +//------------------------------------------------------------------------------ + +bool selectInputDevice(TSoundInputDevice::Source dev) +{ + int mixer; + if ((mixer = openMixer()) < 0) { + close(mixer); + return false; //throw TException("Can't open the mixer device"); + } + int index = isInputDeviceSupported(dev, mixer); + if (index == -1) + return false; + int recsrc; + if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) { + perror("Read recsrc"); + close(mixer); + return false; + } + if (!(recsrc & 1 << index)) { + recsrc = 1 << index; + if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1) { + perror("Write recsrc"); + ::close(mixer); + return false; + } + } + + if (!controlEnableRecord(mixer)) { + close(mixer); + return false; //throw TException("Can't enable recording"); + } + ::close(mixer); + return true; +} +} diff --git a/toonz/sources/common/tsound/tsound_mac.cpp b/toonz/sources/common/tsound/tsound_mac.cpp new file mode 100644 index 0000000..a225dda --- /dev/null +++ b/toonz/sources/common/tsound/tsound_mac.cpp @@ -0,0 +1,803 @@ + + +#include "tsound_t.h" +#include "texception.h" +#include "tthread.h" +#include "tthreadmessage.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +using namespace std; + +//============================================================================== +namespace +{ +TThread::Mutex MutexOut; +} + +class TSoundOutputDeviceImp +{ +public: + bool m_isPlaying; + bool m_looped; + TSoundTrackFormat m_currentFormat; + std::set m_supportedRate; + bool m_opened; + AudioFileID musicFileID; + AudioUnit theOutputUnit; + AudioStreamBasicDescription fileASBD; + AudioStreamBasicDescription outputASBD; + AudioConverterRef converter; + + TSoundOutputDeviceImp() + : m_isPlaying(false), m_looped(false), m_supportedRate(), m_opened(false){}; + + std::set m_listeners; + + ~TSoundOutputDeviceImp(){}; + + bool doOpenDevice(); + bool doSetStreamFormat(const TSoundTrackFormat &format); + bool doStopDevice(); + void play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing); +}; + +//----------------------------------------------------------------------------- +namespace +{ + +struct MyData { + char *entireFileBuffer; + + UInt64 totalPacketCount; + UInt64 fileByteCount; + UInt32 maxPacketSize; + UInt64 packetOffset; + UInt64 byteOffset; + bool m_doNotify; + + void *sourceBuffer; + AudioConverterRef converter; + TSoundOutputDeviceImp *imp; + bool isLooping; + MyData() + : entireFileBuffer(0), totalPacketCount(0), fileByteCount(0), maxPacketSize(0), packetOffset(0), byteOffset(0), sourceBuffer(0), isLooping(false), imp(0), m_doNotify(true) + { + } +}; + +class PlayCompletedMsg : public TThread::Message +{ + std::set m_listeners; + MyData *m_data; + +public: + PlayCompletedMsg(MyData *data) + : m_data(data) + { + } + + TThread::Message *clone() const + { + return new PlayCompletedMsg(*this); + } + + void onDeliver() + { + if (m_data->imp) { + if (m_data->m_doNotify == false) + return; + m_data->m_doNotify = false; + if (m_data->imp->m_isPlaying) + m_data->imp->doStopDevice(); + std::set::iterator it = m_data->imp->m_listeners.begin(); + for (; it != m_data->imp->m_listeners.end(); ++it) + (*it)->onPlayCompleted(); + } + } +}; +} + +#define checkStatus(err) \ + if (err) { \ + printf("Error: 0x%x -> %s: %d\n", (int)err, __FILE__, __LINE__); \ + fflush(stdout); \ + } + +extern "C" { +//This is an example of a Input Procedure from a call to AudioConverterFillComplexBuffer. +//The total amount of data needed is "ioNumberDataPackets" when this method is first called. +//On exit, "ioNumberDataPackets" must be set to the actual amount of data obtained. +//Upon completion, all new input data must point to the AudioBufferList in the parameter ( "ioData" ) +OSStatus MyACComplexInputProc(AudioConverterRef inAudioConverter, + UInt32 *ioNumberDataPackets, + AudioBufferList *ioData, + AudioStreamPacketDescription **outDataPacketDescription, + void *inUserData) +{ + OSStatus err = noErr; + UInt32 bytesCopied = 0; + + MyData *myData = static_cast(inUserData); + + // initialize in case of failure + ioData->mBuffers[0].mData = NULL; + ioData->mBuffers[0].mDataByteSize = 0; + + { + //TThread::ScopedLock sl(MutexOut); + if (myData->imp->m_isPlaying == false) + return noErr; + } + + // if there are not enough packets to satisfy request, then read what's left + if (myData->packetOffset + *ioNumberDataPackets > myData->totalPacketCount) + *ioNumberDataPackets = myData->totalPacketCount - myData->packetOffset; + + // do nothing if there are no packets available + if (*ioNumberDataPackets) { + if (myData->sourceBuffer != NULL) { + free(myData->sourceBuffer); + myData->sourceBuffer = NULL; + } + + //the total amount of data requested by the AudioConverter + bytesCopied = *ioNumberDataPackets * myData->maxPacketSize; + //alloc a small buffer for the AudioConverter to use. + myData->sourceBuffer = (void *)calloc(1, bytesCopied); + //copy the amount of data needed (bytesCopied) from buffer of audio file + memcpy(myData->sourceBuffer, myData->entireFileBuffer + myData->byteOffset, bytesCopied); + + // keep track of where we want to read from next time + myData->byteOffset += *ioNumberDataPackets * myData->maxPacketSize; + myData->packetOffset += *ioNumberDataPackets; + + ioData->mBuffers[0].mData = myData->sourceBuffer; // tell the Audio Converter where it's source data is + ioData->mBuffers[0].mDataByteSize = bytesCopied; // tell the Audio Converter how much data in each buffer + } else { + // there aren't any more packets to read. + // Set the amount of data read (mDataByteSize) to zero + // and return noErr to signal the AudioConverter there are + // no packets left. + + ioData->mBuffers[0].mData = NULL; + ioData->mBuffers[0].mDataByteSize = 0; + delete[] myData->entireFileBuffer; + myData->entireFileBuffer = 0; + err = noErr; + /* + { + TThread::ScopedLock sl(MutexOut); + *(myData->isPlaying) = false; //questo lo faccio nel main thread + } +*/ + PlayCompletedMsg(myData).send(); + } + + return err; +} + +OSStatus MyFileRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData) +{ + MyData *myData = static_cast(inRefCon); + OSStatus err = noErr; + void *inInputDataProcUserData = inRefCon; + AudioStreamPacketDescription *outPacketDescription = NULL; + //To obtain a data buffer of converted data from a complex input source(compressed files, etc.) + //use AudioConverterFillComplexBuffer. The total amount of data requested is "inNumFrames" and + //on return is set to the actual amount of data recieved. + //All converted data is returned to "ioData" (AudioBufferList). + err = AudioConverterFillComplexBuffer(myData->converter, MyACComplexInputProc, inInputDataProcUserData, &inNumFrames, ioData, outPacketDescription); + + /*Parameters for AudioConverterFillComplexBuffer() +converter - the converter being used +ACComplexInputProc() - input procedure to supply data to the Audio Converter +inInputDataProcUserData - Used to hold any data that needs to be passed on. Not needed in this example. +inNumFrames - The amount of requested data. On output, this +number is the amount actually received. +ioData - Buffer of the converted data recieved on return +outPacketDescription - contains the format of the returned data. Not used in this example. +*/ + + //checkStatus(err); + return err; +} + +} //extern "C" + +void PrintStreamDesc(AudioStreamBasicDescription *inDesc) +{ + if (!inDesc) { + printf("Can't print a NULL desc!\n"); + return; + } + + printf("- - - - - - - - - - - - - - - - - - - -\n"); + printf(" Sample Rate:%f\n", inDesc->mSampleRate); + printf(" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char *)&inDesc->mFormatID); + printf(" Format Flags:%lX\n", inDesc->mFormatFlags); + printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); + printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); + printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); + printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); + printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); + printf("- - - - - - - - - - - - - - - - - - - -\n"); +} + +bool TSoundOutputDeviceImp::doOpenDevice() +{ + m_opened = false; + OSStatus err = noErr; + ComponentDescription desc; + Component comp; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + //all Audio Units in AUComponent.h must use "kAudioUnitManufacturer_Apple" as the Manufacturer + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = FindNextComponent(NULL, &desc); //Finds an component that meets the desc spec's + if (comp == NULL) + return false; + err = OpenAComponent(comp, &theOutputUnit); //gains access to the services provided by the component + if (err) + return false; + + UInt32 size; + Boolean outWritable; + UInt32 theInputBus = 0; + //Gets the size of the Stream Format Property and if it is writable + err = AudioUnitGetPropertyInfo(theOutputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &size, &outWritable); + //Get the current stream format of the output + err = AudioUnitGetProperty(theOutputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &outputASBD, &size); + checkStatus(err); + //Set the stream format of the output to match the input + err = AudioUnitSetProperty(theOutputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + theInputBus, + &outputASBD, + size); + checkStatus(err); + + // Initialize AudioUnit, alloc mem buffers for processing + err = AudioUnitInitialize(theOutputUnit); + checkStatus(err); + if (err == noErr) + m_opened = true; + return m_opened; +} + +bool TSoundOutputDeviceImp::doSetStreamFormat(const TSoundTrackFormat &format) +{ + if (!m_opened) + doOpenDevice(); + if (!m_opened) + return false; + + fileASBD.mSampleRate = format.m_sampleRate; + fileASBD.mFormatID = kAudioFormatLinearPCM; + fileASBD.mFormatFlags = 14; + /* +Standard flags: kAudioFormatFlagIsFloat = (1L << 0) +kAudioFormatFlagIsBigEndian = (1L << 1) +kAudioFormatFlagIsSignedInteger = (1L << 2) +kAudioFormatFlagIsPacked = (1L << 3) +kAudioFormatFlagIsAlignedHigh = (1L << 4) +kAudioFormatFlagIsNonInterleaved = (1L << 5) +kAudioFormatFlagsAreAllClear = (1L << 31) + +Linear PCM flags: +kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat +kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian +kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger +kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked +kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh +kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved +kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear +*/ + fileASBD.mBytesPerPacket = (format.m_bitPerSample >> 3) * format.m_channelCount; + fileASBD.mFramesPerPacket = 1; + fileASBD.mBytesPerFrame = (format.m_bitPerSample >> 3) * format.m_channelCount; + fileASBD.mChannelsPerFrame = format.m_channelCount; + fileASBD.mBitsPerChannel = format.m_bitPerSample; + fileASBD.mReserved = 0; + //PrintStreamDesc(&fileASBD); + m_opened = true; + return true; +} + +//============================================================================== + +TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp) +{ + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } +} + +//------------------------------------------------------------------------------ + +TSoundOutputDevice::~TSoundOutputDevice() +{ + stop(); + close(); + delete m_imp; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::installed() +{ + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::open(const TSoundTrackP &st) +{ + if (!m_imp->doOpenDevice()) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Problem to open the output device"); + if (!m_imp->doSetStreamFormat(st->getFormat())) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Problem to open the output device setting some params"); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::close() +{ + stop(); + m_imp->m_opened = false; + AudioUnitUninitialize(m_imp->theOutputUnit); //release resources without closing the component + CloseComponent(m_imp->theOutputUnit); //Terminates your application's access to the services provided + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) +{ + //TThread::ScopedLock sl(MutexOut); + int lastSample = st->getSampleCount() - 1; + notLessThan(0, s0); + notLessThan(0, s1); + + notMoreThan(lastSample, s0); + notMoreThan(lastSample, s1); + + if (s0 > s1) { +#ifdef DEBUG + cout << "s0 > s1; reorder" << endl; +#endif + swap(s0, s1); + } + + if (isPlaying()) { +#ifdef DEBUG + cout << "is playing, stop it!" << endl; +#endif + stop(); + } + m_imp->play(st, s0, s1, loop, scrubbing); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDeviceImp::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) +{ + if (!doSetStreamFormat(st->getFormat())) + return; + + OSStatus err = noErr; + MyData *myData = new MyData(); + + myData->imp = this; + UInt32 magicCookieSize = 0; + //PrintStreamDesc(&outputASBD); + err = AudioConverterNew(&fileASBD, &outputASBD, &converter); + checkStatus(err); + err = AudioFileGetPropertyInfo(musicFileID, + kAudioFilePropertyMagicCookieData, + &magicCookieSize, + NULL); + + if (err == noErr) { + void *magicCookie = calloc(1, magicCookieSize); + if (magicCookie) { + //Get Magic Cookie data from Audio File + err = AudioFileGetProperty(musicFileID, + kAudioFilePropertyMagicCookieData, + &magicCookieSize, + magicCookie); + + // Give the AudioConverter the magic cookie decompression params if there are any + if (err == noErr) { + err = AudioConverterSetProperty(myData->converter, + kAudioConverterDecompressionMagicCookie, + magicCookieSize, + magicCookie); + } + err = noErr; + if (magicCookie) + free(magicCookie); + } + } else //this is OK because some audio data doesn't need magic cookie data + err = noErr; + + checkStatus(err); + myData->converter = converter; + myData->totalPacketCount = s1 - s0; + myData->fileByteCount = (s1 - s0) * st->getSampleSize(); + myData->entireFileBuffer = new char[myData->fileByteCount]; + +#if defined(i386) + if (st->getBitPerSample() == 16) { + int i; + USHORT *dst = (USHORT *)(myData->entireFileBuffer); + USHORT *src = (USHORT *)(st->getRawData() + s0 * st->getSampleSize()); + + for (i = 0; i < myData->fileByteCount / 2; i++) + *dst++ = swapUshort(*src++); + } else + memcpy(myData->entireFileBuffer, st->getRawData() + s0 * st->getSampleSize(), myData->fileByteCount); +#else + memcpy(myData->entireFileBuffer, st->getRawData() + s0 * st->getSampleSize(), myData->fileByteCount); +#endif + + myData->maxPacketSize = fileASBD.mFramesPerPacket * fileASBD.mBytesPerFrame; + { + //TThread::ScopedLock sl(MutexOut); + m_isPlaying = true; + } + myData->isLooping = loop; + + //cout << "total packet count = " << myData->totalPacketCount <fileByteCount << endl; + + AURenderCallbackStruct renderCallback; + memset(&renderCallback, 0, sizeof(AURenderCallbackStruct)); + + renderCallback.inputProc = MyFileRenderProc; + renderCallback.inputProcRefCon = myData; + + //Sets the callback for the Audio Unit to the renderCallback + err = AudioUnitSetProperty(theOutputUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, &renderCallback, + sizeof(AURenderCallbackStruct)); + + checkStatus(err); + + err = AudioOutputUnitStart(theOutputUnit); + + checkStatus(err); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDeviceImp::doStopDevice() +{ + m_isPlaying = false; + AudioOutputUnitStop(theOutputUnit); //you must stop the audio unit from processing + AudioConverterDispose(converter); //deallocates the memory used by inAudioConverter + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::stop() +{ + //TThread::ScopedLock sl(MutexOut); + if (m_imp->m_opened == false) + return; + + //TThread::ScopedLock sl(MutexOut); + m_imp->doStopDevice(); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::attach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.insert(listener); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::detach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.erase(listener); +} + +//------------------------------------------------------------------------------ + +double TSoundOutputDevice::getVolume() +{ + if (!m_imp->m_opened) + m_imp->doOpenDevice(); + + Float32 leftVol, rightVol; + AudioUnitGetParameter( + m_imp->theOutputUnit, + kHALOutputParam_Volume, + kAudioUnitScope_Output, + 0, + &leftVol); + + AudioUnitGetParameter( + m_imp->theOutputUnit, + kHALOutputParam_Volume, + kAudioUnitScope_Output, + 0, + &rightVol); + double vol = (leftVol + rightVol) / 2; + + return (vol < 0. ? 0. : vol); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::setVolume(double volume) +{ + Float32 vol = volume; + AudioUnitSetParameter( + m_imp->theOutputUnit, + kHALOutputParam_Volume, + kAudioUnitScope_Output, + 0, + vol, + 0); + + AudioUnitSetParameter( + m_imp->theOutputUnit, + kHALOutputParam_Volume, + kAudioUnitScope_Output, + 0, + vol, + 0); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::supportsVolume() +{ + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isPlaying() const +{ + // TThread::ScopedLock sl(MutexOut); + return m_imp->m_isPlaying; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isLooping() +{ + //TThread::ScopedLock sl(MutexOut); + return m_imp->m_looped; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::setLooping(bool loop) +{ + //TThread::ScopedLock sl(MutexOut); + m_imp->m_looped = loop; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt( + sampleRate, + bitPerSample, + channelCount, + true); + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + // try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + /*} + catch (TSoundDeviceException &e) { + throw TSoundDeviceException( e.getType(), e.getMessage()); + }*/ +} + +//============================================================================== +//============================================================================== +// REGISTRAZIONE +//============================================================================== +//============================================================================== + +class TSoundInputDeviceImp +{ +public: + //ALport m_port; + bool m_stopped; + bool m_isRecording; + bool m_oneShotRecording; + + long m_recordedSampleCount; + + TSoundTrackFormat m_currentFormat; + TSoundTrackP m_st; + std::set m_supportedRate; + + TThread::Executor m_executor; + + TSoundInputDeviceImp() + : m_stopped(false), m_isRecording(false) + // , m_port(NULL) + , + m_oneShotRecording(false), m_recordedSampleCount(0), m_st(0), m_supportedRate(){}; + + ~TSoundInputDeviceImp(){}; + + bool doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType); +}; + +bool TSoundInputDeviceImp::doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType) +{ + return true; +} + +//============================================================================== + +class RecordTask : public TThread::Runnable +{ +public: + TSoundInputDeviceImp *m_devImp; + int m_ByteToSample; + + RecordTask(TSoundInputDeviceImp *devImp, int numByte) + : TThread::Runnable(), m_devImp(devImp), m_ByteToSample(numByte){}; + + ~RecordTask(){}; + + void run(); +}; + +void RecordTask::run() +{ +} + +//============================================================================== + +TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp) +{ +} + +//------------------------------------------------------------------------------ + +TSoundInputDevice::~TSoundInputDevice() +{ + /* + if(m_imp->m_port) + alClosePort(m_imp->m_port); + delete m_imp; + */ +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::installed() +{ + /* + if (alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, 0, 0, 0, 0) <=0) + return false; + */ + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackFormat &format, TSoundInputDevice::Source type) +{ +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackP &st, TSoundInputDevice::Source type) +{ +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundInputDevice::stop() +{ + TSoundTrackP st; + return st; +} + +//------------------------------------------------------------------------------ + +double TSoundInputDevice::getVolume() +{ + return 0.0; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::setVolume(double volume) +{ + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::supportsVolume() +{ + return true; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + /* + try { + */ + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + /*} + + catch (TSoundDeviceException &e) { + throw TSoundDeviceException( e.getType(), e.getMessage()); + } + */ +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::isRecording() +{ + return m_imp->m_isRecording; +} diff --git a/toonz/sources/common/tsound/tsound_nt.cpp b/toonz/sources/common/tsound/tsound_nt.cpp new file mode 100644 index 0000000..d1c8e94 --- /dev/null +++ b/toonz/sources/common/tsound/tsound_nt.cpp @@ -0,0 +1,2355 @@ + + +#include "tsound.h" + +#ifndef TNZCORE_LIGHT +#include "tthread.h" +#endif +#include "tsop.h" +#include +#include "tsystem.h" + +//========================================================= + +//forward declarations +class TSoundOutputDeviceImp; +class TSoundInputDeviceImp; + +//========================================================= + +namespace +{ +void CALLBACK recordCB( + HWAVEIN hwi, + UINT uMsg, + DWORD dwInstance, + DWORD dwParam1, + DWORD dwParam2); + +bool setRecordLine(TSoundInputDevice::Source typeInput); + +MMRESULT getLineInfo( + HMIXEROBJ hMixer, + DWORD dwComponentType, + MIXERLINE &mxl); + +MMRESULT getLineControl( + MIXERCONTROL &mxc, + HMIXEROBJ hMixer, + DWORD dwLineID, + DWORD dwControlType); + +MMRESULT setControlDetails( + HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue); + +MMRESULT getControlDetails( + HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue); + +MMRESULT isaFormatSupported( + int sampleRate, int channelCount, int bitPerSample, bool input); + +DWORD WINAPI MyWaveOutCallbackThread(LPVOID lpParameter); +void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample); +} + +//============================================================================== +// Class to send the message that a playback is completed +//============================================================================== +#ifndef TNZCORE_LIGHT +class EndPlayMsg : public TThread::Message +{ +public: + EndPlayMsg(TSoundOutputDeviceListener *notifier) + { + m_listener = notifier; + } + + TThread::Message *clone() const + { + return new EndPlayMsg(*this); + } + + void onDeliver() + { + m_listener->onPlayCompleted(); + } + +private: + TSoundOutputDeviceListener *m_listener; +}; +#endif + +int makeDWORD(const short lo, const short hi) +{ + int dw = hi << 16; + dw |= lo; + return dw; +} + +//============================================================================== +class WavehdrQueue; + +class TSoundOutputDeviceImp +{ +public: + HWAVEOUT m_wout; + WavehdrQueue *m_whdrQueue; + TSoundTrackFormat m_currentFormat; + std::set m_supportedRate; + + TThread::Mutex m_mutex; + + bool m_stopped; + bool m_isPlaying; + bool m_looped; + bool m_scrubbing; + + std::set m_listeners; + DWORD m_notifyThreadId; + + HANDLE m_closeDevice; + + TSoundOutputDeviceImp(); + ~TSoundOutputDeviceImp(); + + bool doOpenDevice(const TSoundTrackFormat &format); + bool doPlay(WAVEHDR *whdr, const TSoundTrackFormat format); + bool doCloseDevice(); + + bool verifyRate(); + void insertAllRate(); +}; + +//============================================================================== + +class WavehdrQueue +{ +public: + WavehdrQueue(TSoundOutputDeviceImp *devImp, int slotCount) + : m_devImp(devImp), m_items(), m_queuedItems(), m_slotCount(slotCount), m_mutex(), m_lastOffset(0) + { + } + + ~WavehdrQueue() {} + + void put(TSoundTrackP &subTrack); + WAVEHDR *get(); + bool popFront(int count); + void pushBack(WAVEHDR *whdr, TSoundTrackP st); + int size(); + void clear(); + bool isAllQueuedItemsPlayed(); + +private: + std::list> m_items; + std::list m_queuedItems; + + TThread::Mutex m_mutex; + int m_slotCount; + int m_lastOffset; + TSoundOutputDeviceImp *m_devImp; + TSoundTrackP m_lastTrack; +}; + +//============================================================================== + +WAVEHDR *prepareWaveHeader(HWAVEOUT wout, const TSoundTrackP &subTrack, ULONG &count) +{ + WAVEHDR *whdr = new WAVEHDR; + memset(whdr, 0, sizeof(WAVEHDR)); + whdr->dwBufferLength = subTrack->getSampleSize() * subTrack->getSampleCount(); + whdr->lpData = new char[whdr->dwBufferLength]; + whdr->dwFlags = 0; + whdr->dwUser = count; + memcpy(whdr->lpData, subTrack->getRawData(), whdr->dwBufferLength); + + MMRESULT ret = waveOutPrepareHeader(wout, whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) { + delete[] whdr->lpData; + delete whdr; + return 0; + } + ++count; + return whdr; +} + +//============================================================================== + +void WavehdrQueue::put(TSoundTrackP &subTrack) +{ + assert(subTrack->getRawData()); + static ULONG count = 1; + + //codice messo per tab: facendo il play al rilascio del mouse e su piu' + //colonne in cui le traccie potrebbe avere diversi formati siccome qui in + //alcune situazioni si fa subito waveOutWrite c'e' bisogno di controllare + //se il formato con cui e' stato aperto in precedenza il device e' uguale + //a quello della traccia + if (m_devImp->m_wout && m_devImp->m_currentFormat != subTrack->getFormat()) { + m_devImp->doCloseDevice(); + TSystem::sleep(300); + m_devImp->doOpenDevice(subTrack->getFormat()); + } + + TThread::MutexLocker sl(&m_mutex); + if (!m_devImp->m_scrubbing) { + WAVEHDR *whdr2 = 0; + + //traccia + whdr2 = prepareWaveHeader(m_devImp->m_wout, subTrack, count); + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + + MMRESULT ret = MMSYSERR_NOERROR; + + if (whdr2 && whdr2->dwFlags & WHDR_PREPARED) { + ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr2, subTrack); + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + TThread::MutexLocker sl(&m_devImp->m_mutex); + m_devImp->m_isPlaying = true; + m_devImp->m_stopped = false; + } + } + return; + } + + if (m_queuedItems.size() == 0) { + WAVEHDR *whdr1 = 0; + WAVEHDR *whdr2 = 0; + WAVEHDR *whdr3 = 0; + + MMRESULT ret; + TSoundTrackP riseTrack, decayTrack; + int sampleSize = subTrack->getSampleSize(); + + //cresce + riseTrack = TSop::fadeIn(subTrack, 0.9); + whdr1 = prepareWaveHeader(m_devImp->m_wout, riseTrack, count); + + //traccia + whdr2 = prepareWaveHeader(m_devImp->m_wout, subTrack, count); + + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + + //decresce + decayTrack = 0; + if (m_lastOffset) { + decayTrack = TSop::fadeOut(subTrack, 0.9); + whdr3 = prepareWaveHeader(m_devImp->m_wout, decayTrack, count); + } + + if (whdr1 && (whdr1->dwFlags & WHDR_PREPARED)) { + ret = waveOutWrite(m_devImp->m_wout, whdr1, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr1, riseTrack); + getAmplitude(m_lastOffset, riseTrack, riseTrack->getSampleCount() - 1L); + } + } + if (whdr2 && (whdr2->dwFlags & WHDR_PREPARED)) { + ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr2, subTrack); + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + TThread::MutexLocker sl(&m_devImp->m_mutex); + m_devImp->m_isPlaying = true; + m_devImp->m_stopped = false; + } + } + if (whdr3 && (whdr3->dwFlags & WHDR_PREPARED)) { + ret = waveOutWrite(m_devImp->m_wout, whdr3, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr3, decayTrack); + if (decayTrack->isSampleSigned()) + m_lastOffset = 0; + else + m_lastOffset = 127; + } + } + return; + } + + if (m_queuedItems.size() < 10) { + WAVEHDR *whdr1 = 0; + WAVEHDR *whdr2 = 0; + WAVEHDR *whdr = new WAVEHDR; + memset(whdr, 0, sizeof(WAVEHDR)); + whdr->dwBufferLength = subTrack->getSampleSize() * subTrack->getSampleCount(); + whdr->lpData = new char[whdr->dwBufferLength]; + whdr->dwFlags = 0; + memcpy(whdr->lpData, subTrack->getRawData(), whdr->dwBufferLength); + int sampleSize = subTrack->getSampleSize(); + TSoundTrackP riseTrack = 0; + + if (m_lastOffset) ///devo fare ilcross fade + { + int offset; + getAmplitude(offset, subTrack, 0L); + + offset = m_lastOffset - offset; + if (offset) { + TSoundTrackP st = TSop::crossFade(m_lastTrack, subTrack, 0.3); + memcpy(whdr->lpData, st->getRawData(), st->getSampleCount() * sampleSize); + } + } else // e' zero ma ne metto uno che cresce faccio il fadeIn + { + riseTrack = TSop::fadeIn(subTrack, 0.3); + whdr1 = prepareWaveHeader(m_devImp->m_wout, riseTrack, count); + } + + whdr->dwUser = count; + ++count; + MMRESULT ret = waveOutPrepareHeader(m_devImp->m_wout, whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) { + delete[] whdr->lpData; + delete whdr; + return; + } + + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + + TSoundTrackP decayTrack = 0; + if (m_queuedItems.size() <= 7) { + if (m_lastOffset) //devo fare il fadeOut + { + decayTrack = TSop::fadeOut(subTrack, 0.3); + whdr2 = prepareWaveHeader(m_devImp->m_wout, decayTrack, count); + } + } + + if (whdr1 && whdr1->dwFlags & WHDR_PREPARED) { + ret = waveOutWrite(m_devImp->m_wout, whdr1, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr1, riseTrack); + getAmplitude(m_lastOffset, riseTrack, riseTrack->getSampleCount() - 1L); + } + } + + if (whdr && whdr->dwFlags & WHDR_PREPARED) { + ret = waveOutWrite(m_devImp->m_wout, whdr, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr, subTrack); + getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); + { + TThread::MutexLocker sl(&m_devImp->m_mutex); + m_devImp->m_isPlaying = true; + m_devImp->m_stopped = false; + } + } + } + + if (whdr2 && whdr2->dwFlags & WHDR_PREPARED) { + ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + pushBack(whdr2, decayTrack); + if (decayTrack->isSampleSigned()) + m_lastOffset = 0; + else + m_lastOffset = 127; + } + } + return; + } + + if ((int)m_items.size() == m_slotCount) { + WAVEHDR *item = m_items.front().first; + MMRESULT ret = waveOutUnprepareHeader( + m_devImp->m_wout, item, sizeof(WAVEHDR)); + + if (ret == MMSYSERR_NOERROR) { + delete[] item->lpData; + delete item; + } + + m_items.pop_front(); + + if (m_queuedItems.size() != 0) { + WAVEHDR *item = m_items.front().first; + int sampleSize = m_items.front().second->getSampleSize(); + int offset; + getAmplitude(offset, m_items.front().second, 0L); + + offset = m_lastOffset - offset; + if (offset) { + TSoundTrackP st = TSop::crossFade( + m_lastTrack, m_items.front().second, 0.3); + memcpy(item->lpData, st->getRawData(), st->getSampleCount() * sampleSize); + } + } + } + + WAVEHDR *whdr = prepareWaveHeader(m_devImp->m_wout, subTrack, count); + + assert(whdr && whdr->dwFlags & WHDR_PREPARED); + m_items.push_back(std::make_pair(whdr, subTrack)); + assert((int)m_items.size() <= m_slotCount); +} + +//---------------------------------------------------------------------------- + +// restituisce il piu' vecchio WAVEHDR il cui stato e' prepared && !done +WAVEHDR *WavehdrQueue::get() +{ + TThread::MutexLocker sl(&m_mutex); + if (m_items.size() == 0) + return 0; + + WAVEHDR *whdr = m_items.front().first; + assert(whdr->dwFlags & WHDR_PREPARED); + + pushBack(whdr, m_items.front().second); + getAmplitude( + m_lastOffset, m_items.front().second, m_items.front().second->getSampleCount() - 1L); + m_items.pop_front(); + + return whdr; +} + +//----------------------------------------------------------------------------- + +// rimuove dalla coda il piu' vecchio WAVEHDR il cui stato e' done +bool WavehdrQueue::popFront(int count) +{ + TThread::MutexLocker sl(&m_mutex); + //assert(m_queuedItems.size() > 0); + if (m_queuedItems.size() <= 0) + return false; + WAVEHDR *whdr = m_queuedItems.front(); + // controllo introdotto pr via che su win2k si perde alcune + // notifiche di WHDR_DONE + while ((DWORD)count > whdr->dwUser) { + MMRESULT ret = waveOutUnprepareHeader( + m_devImp->m_wout, whdr, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + m_queuedItems.pop_front(); + delete[] whdr->lpData; + delete whdr; + whdr = m_queuedItems.front(); + } + } + assert(whdr->dwFlags & WHDR_DONE); + m_queuedItems.pop_front(); + delete[] whdr->lpData; + delete whdr; + return true; +} + +//----------------------------------------------------------------------------- + +void WavehdrQueue::pushBack(WAVEHDR *whdr, TSoundTrackP st) +{ + TThread::MutexLocker sl(&m_mutex); + m_queuedItems.push_back(whdr); + m_lastTrack = st; +} + +//----------------------------------------------------------------------------- + +int WavehdrQueue::size() +{ + TThread::MutexLocker sl(&m_mutex); + int size = m_queuedItems.size(); + return size; +} + +//----------------------------------------------------------------------------- + +void WavehdrQueue::clear() +{ + TThread::MutexLocker sl(&m_mutex); + m_items.clear(); + m_lastTrack = TSoundTrackP(); + std::list::iterator it; + for (it = m_queuedItems.begin(); it != m_queuedItems.end(); it++) { + WAVEHDR *wvhdr = *it; + delete[] wvhdr->lpData; + delete wvhdr; + } + m_queuedItems.clear(); +} + +//----------------------------------------------------------------------------- + +bool WavehdrQueue::isAllQueuedItemsPlayed() +{ + std::list::iterator it; + bool finished = true; + for (it = m_queuedItems.begin(); it != m_queuedItems.end(); it++) { + WAVEHDR *wvhdr = *it; + finished = finished && (wvhdr->dwFlags & WHDR_DONE); + } + return finished; +} + +//============================================================================== + +TSoundOutputDeviceImp::TSoundOutputDeviceImp() + : m_stopped(true), m_isPlaying(false), m_looped(false), m_scrubbing(false), m_wout(0) +{ + m_whdrQueue = new WavehdrQueue(this, 4); + + insertAllRate(); + if (!verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::FailedInit, + "Unable to verify supported rates"); + + m_closeDevice = CreateEvent( + NULL, // no security attributes + FALSE, // auto-reset event + FALSE, // initial state is not signaled + NULL); // object not named +} + +//---------------------------------------------------------------------------- + +TSoundOutputDeviceImp::~TSoundOutputDeviceImp() +{ + delete m_whdrQueue; +} + +//---------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) +{ + WAVEFORMATEX wf; + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = format.m_channelCount; + wf.nSamplesPerSec = format.m_sampleRate; + wf.wBitsPerSample = format.m_bitPerSample; + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = 0; + + TThread::MutexLocker sl(&m_mutex); + CloseHandle(CreateThread(NULL, 0, MyWaveOutCallbackThread, (LPVOID) this, 0, &m_notifyThreadId)); + + MMRESULT ret; + if ((ret = waveOutOpen(&m_wout, WAVE_MAPPER, + &wf, (DWORD)m_notifyThreadId, (DWORD) this, CALLBACK_THREAD)) != MMSYSERR_NOERROR) { + while (!PostThreadMessage(m_notifyThreadId, WM_QUIT, 0, 0)) + ; + } + if (ret != MMSYSERR_NOERROR) + return false; + + if (ret != MMSYSERR_NOERROR) + return false; + m_currentFormat = format; + return (ret == MMSYSERR_NOERROR); +} + +//---------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::doPlay(WAVEHDR *whdr, const TSoundTrackFormat format) +{ + TThread::MutexLocker sl(&m_mutex); + if (!m_wout || (m_wout && m_currentFormat != format)) + doOpenDevice(format); + + MMRESULT ret; + ret = waveOutWrite(m_wout, whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) + return false; + m_stopped = false; + m_isPlaying = true; + + return true; +} + +//---------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::doCloseDevice() +{ + TThread::MutexLocker sl(&m_mutex); + if (m_wout) { + MMRESULT ret = waveOutReset(m_wout); + if (ret != MMSYSERR_NOERROR) + return false; + ret = waveOutClose(m_wout); + if (ret != MMSYSERR_NOERROR) + return false; + m_wout = 0; + m_stopped = true; + m_isPlaying = false; + m_looped = false; + } + + PostThreadMessage(m_notifyThreadId, WM_QUIT, 0, 0); + return true; +} + +//---------------------------------------------------------------------------- + +void TSoundOutputDeviceImp::insertAllRate() +{ + m_supportedRate.insert(8000); + m_supportedRate.insert(11025); + m_supportedRate.insert(16000); + m_supportedRate.insert(22050); + m_supportedRate.insert(32000); + m_supportedRate.insert(44100); + m_supportedRate.insert(48000); +} + +//---------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::verifyRate() +{ + std::set::iterator it; + + for (it = m_supportedRate.begin(); + it != m_supportedRate.end();) { + MMRESULT ret; + WAVEFORMATEX wf; + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 1; + wf.nSamplesPerSec = *it; + wf.wBitsPerSample = 8; + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = 0; + + ret = waveOutOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); + + if (ret == MMSYSERR_NOERROR) { + ++it; + continue; + } + if (ret == WAVERR_BADFORMAT) + it = m_supportedRate.erase(it); + else + return false; + } + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + + return true; +} + +//============================================================================== + +TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp()) +{ +} + +//------------------------------------------------------------------------------ + +TSoundOutputDevice::~TSoundOutputDevice() +{ + close(); + WaitForSingleObject(m_imp->m_closeDevice, INFINITE); + CloseHandle(m_imp->m_closeDevice); + delete m_imp; +} + +//------------------------------------------------------------------------------ + +namespace +{ +DWORD WINAPI MyWaveOutCallbackThread(LPVOID lpParameter) +{ + TSoundOutputDeviceImp *devImp = (TSoundOutputDeviceImp *)lpParameter; + if (!devImp) + return 0; + + MSG msg; + BOOL bRet; + while (bRet = GetMessage(&msg, NULL, 0, 0)) { + if (bRet == -1) { + // si e' verificato un errore + break; + } + + switch (msg.message) { + case MM_WOM_DONE: { + WAVEHDR *pWaveHdr = ((WAVEHDR *)msg.lParam); + { + TThread::MutexLocker sl(&devImp->m_mutex); + if (devImp->m_looped) { + devImp->doPlay(pWaveHdr, devImp->m_currentFormat); + continue; + } + } + + WAVEHDR *whdr = 0; + if (devImp->m_whdrQueue->popFront(pWaveHdr->dwUser)) { + whdr = devImp->m_whdrQueue->get(); + if (whdr) + devImp->doPlay(whdr, devImp->m_currentFormat); + + WaitForSingleObject(devImp->m_closeDevice, INFINITE); + CloseHandle(devImp->m_closeDevice); + + MMRESULT ret = waveOutUnprepareHeader( + devImp->m_wout, pWaveHdr, sizeof(WAVEHDR)); + if (ret == MMSYSERR_NOERROR) { + delete pWaveHdr->lpData; + delete pWaveHdr; + } + } + + if (!whdr && devImp->m_whdrQueue->size() == 0) { + std::set::iterator it = devImp->m_listeners.begin(); + for (; it != devImp->m_listeners.end(); ++it) { +#ifndef TNZCORE_LIGHT + EndPlayMsg *event = new EndPlayMsg(*it); + event->send(); +#else + assert(false); +#endif + } + devImp->doCloseDevice(); + } + + continue; + } + + case MM_WOM_CLOSE: + break; + + default: + continue; + } + } + + SetEvent(devImp->m_closeDevice); + return 0; +} + +void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample) +{ + TSoundTrackP snd = st; + amplitude = 0; + int k = 0; + for (k = 0; k < snd->getChannelCount(); ++k) + amplitude += (int)snd->getPressure(sample, k); + amplitude /= k; +} +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::installed() +{ + int ndev = waveOutGetNumDevs(); + if (ndev <= 0) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::open(const TSoundTrackP &st) +{ + return m_imp->doOpenDevice(st->getFormat()); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::attach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.insert(listener); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::detach(TSoundOutputDeviceListener *listener) +{ + m_imp->m_listeners.erase(listener); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::play( + const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) +{ + assert((scrubbing && !loop) || !scrubbing); + if (!st->getSampleCount()) + return; + TSoundTrackFormat format; + TSoundTrackP subTrack; + + { + TThread::MutexLocker sl(&m_imp->m_mutex); + if (m_imp->m_looped) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Unable to queue another playback when the sound player is looping"); + + format = st->getFormat(); + try { + TSoundTrackFormat fmt = getPreferredFormat(format); + if (fmt != format) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + assert(s1 >= s0); + subTrack = st->extract(s0, s1); + m_imp->m_looped = loop; + m_imp->m_scrubbing = scrubbing; + } + + if (!m_imp->m_wout) + m_imp->doOpenDevice(format); + + m_imp->m_whdrQueue->put(subTrack); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::stop() +{ + if ((m_imp->m_wout) && m_imp->m_isPlaying) { + MMRESULT ret = waveOutReset(m_imp->m_wout); + if (ret != MMSYSERR_NOERROR) + return; + + TThread::MutexLocker sl(&m_imp->m_mutex); + m_imp->m_looped = false; + m_imp->m_stopped = true; + m_imp->m_isPlaying = false; + m_imp->m_whdrQueue->clear(); + } +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::close() +{ + return m_imp->doCloseDevice(); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isPlaying() const +{ + return m_imp->m_isPlaying; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isAllQueuedItemsPlayed() +{ + return m_imp->m_whdrQueue->isAllQueuedItemsPlayed(); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isLooping() +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + return m_imp->m_looped; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::setLooping(bool loop) +{ + TThread::MutexLocker sl(&m_imp->m_mutex); + m_imp->m_looped = loop; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + + //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini + std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported format"); + } else + sampleRate = *it; + + if (bitPerSample <= 8) + bitPerSample = 8; + else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) + bitPerSample = 16; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + + //switch mono/stereo + if (channelCount <= 1) + channelCount = 1; + else + channelCount = 2; + + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } +} + +//============================================================================== +//============================================================================== +// Classi per la gestione della registrazione +//============================================================================== +//============================================================================== + +//============================================================================== + +class WaveFormat : public WAVEFORMATEX +{ +public: + WaveFormat(){}; + WaveFormat(unsigned char channelCount, + unsigned TINT32 sampleRate, + unsigned char bitPerSample); + + ~WaveFormat(){}; +}; + +WaveFormat::WaveFormat(unsigned char channelCount, + unsigned TINT32 sampleRate, + unsigned char bitPerSample) +{ + wFormatTag = WAVE_FORMAT_PCM; + nChannels = channelCount; + nSamplesPerSec = sampleRate; + wBitsPerSample = bitPerSample; + nBlockAlign = (channelCount * bitPerSample) >> 3; + nAvgBytesPerSec = nBlockAlign * sampleRate; + cbSize = 0; +} + +//============================================================================== + +class WinSoundInputDevice +{ +public: + WinSoundInputDevice(); + ~WinSoundInputDevice(); + + void open(const WaveFormat &wf); + void close(); + + void prepareHeader(char *sampleBuffer, + unsigned TINT32 sampleBufferSize, + WAVEHDR &whdr); + + void unprepareHeader(WAVEHDR &whdr); + + void addBlock(WAVEHDR &whdr); + + void start(); + + void reset(); + void stop(); + + HANDLE m_hBlockDone; + +private: + HWAVEIN m_hWaveIn; +}; + +//-------------------------------------------------------------------- + +WinSoundInputDevice::WinSoundInputDevice() + : m_hWaveIn(0) +{ + m_hBlockDone = CreateEvent( + NULL, // no security attributes + FALSE, // auto-reset event + FALSE, // initial state is not signaled + NULL); // object not named +} + +//-------------------------------------------------------------------- + +WinSoundInputDevice::~WinSoundInputDevice() +{ + CloseHandle(m_hBlockDone); +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::open(const WaveFormat &wf) +{ + if (m_hWaveIn) + close(); + + MMRESULT ret = waveInOpen( + &m_hWaveIn, WAVE_MAPPER, + &wf, (DWORD)recordCB, + (DWORD)m_hBlockDone, CALLBACK_FUNCTION); + + if (ret != MMSYSERR_NOERROR) { + throw TException("Error to open the input device"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::close() +{ + if (!m_hWaveIn) + return; + + MMRESULT ret = waveInClose(m_hWaveIn); + + if (ret != MMSYSERR_NOERROR) { + throw TException("Error to close the input device"); + } + m_hWaveIn = 0; +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::prepareHeader(char *sampleBuffer, + unsigned TINT32 sampleBufferSize, + WAVEHDR &whdr) +{ + whdr.lpData = sampleBuffer; + whdr.dwBufferLength = sampleBufferSize; // numero di byte + whdr.dwFlags = 0; + whdr.dwLoops = 0; + + MMRESULT ret = waveInPrepareHeader(m_hWaveIn, &whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to prepare a wave header"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::unprepareHeader(WAVEHDR &whdr) +{ + MMRESULT ret = waveInUnprepareHeader(m_hWaveIn, &whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to unprepare a wave header"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::addBlock(WAVEHDR &whdr) +{ + MMRESULT ret = waveInAddBuffer(m_hWaveIn, &whdr, sizeof(WAVEHDR)); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to add a waveheader"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::start() +{ + int ret = waveInStart(m_hWaveIn); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to add a waveheader"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::reset() +{ + if (!m_hWaveIn) + return; + + MMRESULT ret = waveInReset(m_hWaveIn); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to add a waveheader"); + } +} + +//-------------------------------------------------------------------- + +void WinSoundInputDevice::stop() +{ + MMRESULT ret = waveInStop(m_hWaveIn); + if (ret != MMSYSERR_NOERROR) { + throw TException("Unable to add a waveheader"); + } +} + +//==================================================================== + +#ifndef TNZCORE_LIGHT +class RecordTask : public TThread::Runnable +{ +public: + RecordTask(TSoundInputDeviceImp *dev) + : Runnable(), m_dev(dev) {} + + ~RecordTask() {} + + void run(); + + TSoundInputDeviceImp *m_dev; +}; + +#endif + +//==================================================================== + +class TSoundInputDeviceImp : public WinSoundInputDevice +{ +public: + bool m_allocateBuff; + bool m_isRecording; + bool m_supportVolume; + int m_index; + TINT32 m_byteRecorded; + + TSoundTrackP m_st; + TSoundTrackFormat m_format; + +#ifndef TNZCORE_LIGHT + TThread::Executor m_executor; +#endif + vector m_whdr; + vector m_recordedBlocks; + std::set m_supportedRate; + + HANDLE m_hLastBlockDone; + + TSoundInputDeviceImp(); + ~TSoundInputDeviceImp(); + + void insertAllRate(); + bool verifyRate(); +}; + +//------------------------------------------------------------------------------ + +TSoundInputDeviceImp::TSoundInputDeviceImp() + : m_allocateBuff(false), m_isRecording(false), m_supportVolume(false), m_index(0), m_byteRecorded(0), m_format(), m_whdr(3), m_recordedBlocks(), m_supportedRate() +{ + m_hLastBlockDone = CreateEvent( + NULL, // no security attributes + FALSE, // is manual-reset event? + FALSE, // is signaled? + NULL); // object not named +} + +//------------------------------------------------------------------------------ + +TSoundInputDeviceImp::~TSoundInputDeviceImp() +{ + if (m_isRecording) { + try { + reset(); + WaitForSingleObject(m_hLastBlockDone, INFINITE); + int i; + for (i = 0; i < (int)m_recordedBlocks.size(); ++i) + delete[] m_recordedBlocks[i]; + close(); + } catch (TException &) { + } + } + CloseHandle(m_hLastBlockDone); +} + +//---------------------------------------------------------------------------- + +void TSoundInputDeviceImp::insertAllRate() +{ + m_supportedRate.insert(8000); + m_supportedRate.insert(11025); + m_supportedRate.insert(16000); + m_supportedRate.insert(22050); + m_supportedRate.insert(32000); + m_supportedRate.insert(44100); + m_supportedRate.insert(48000); +} + +//---------------------------------------------------------------------------- + +bool TSoundInputDeviceImp::verifyRate() +{ + std::set::iterator it; + + for (it = m_supportedRate.begin(); + it != m_supportedRate.end();) { + MMRESULT ret; + WAVEFORMATEX wf; + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 1; + wf.nSamplesPerSec = *it; + wf.wBitsPerSample = 8; + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = 0; + + ret = waveInOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); + + if (ret == MMSYSERR_NOERROR) { + ++it; + continue; + } + if (ret == WAVERR_BADFORMAT) + it = m_supportedRate.erase(it); + else + return false; + } + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + + return true; +} + +//==================================================================== +namespace +{ +void CALLBACK recordCB( + HWAVEIN hwi, + UINT uMsg, + DWORD dwInstance, + DWORD dwParam1, + DWORD dwParam2) +{ + WAVEHDR *whdr = (WAVEHDR *)dwParam1; + HANDLE *blockDone = (HANDLE *)dwInstance; + + if (uMsg != MM_WIM_DATA) + return; + SetEvent(blockDone); +} +} + +//============================================================================== + +TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp()) +{ + m_imp->insertAllRate(); + if (!m_imp->verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::FailedInit, + "Unable to verify supported rates"); + if (supportsVolume()) + m_imp->m_supportVolume = true; +} + +//------------------------------------------------------------------------------ + +TSoundInputDevice::~TSoundInputDevice() +{ + delete m_imp; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::installed() +{ + int ndev = waveInGetNumDevs(); + if (ndev <= 0) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +#ifndef TNZCORE_LIGHT + +void TSoundInputDevice::record(const TSoundTrackFormat &format, Source devtype) +{ + if (m_imp->m_isRecording) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Just another recoding is in progress"); + + /*if ((format.m_bitPerSample == 8 && format.m_signedSample) || + (format.m_bitPerSample == 24)) + throw TException("This format is not supported for recording");*/ + try { + TSoundTrackFormat fmt = getPreferredFormat(format); + if (fmt != format) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + if (!setRecordLine(devtype)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Problems to set input source line to record"); + + m_imp->m_format = format; + m_imp->m_st = 0; + ResetEvent(m_imp->m_hLastBlockDone); + ResetEvent(m_imp->m_hBlockDone); + m_imp->m_allocateBuff = true; + m_imp->m_isRecording = true; + m_imp->m_index = 0; + m_imp->m_recordedBlocks.clear(); + m_imp->m_byteRecorded = 0; + TINT32 bytePerSec = format.m_sampleRate * ((format.m_bitPerSample * format.m_channelCount) >> 3); + + try { + WaveFormat wf(m_imp->m_format.m_channelCount, + m_imp->m_format.m_sampleRate, + m_imp->m_format.m_bitPerSample); + + m_imp->open(wf); + } catch (TException &e) { + m_imp->m_isRecording = false; + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, e.getMessage()); + } + for (; m_imp->m_index < (int)(m_imp->m_whdr.size() - 1); ++m_imp->m_index) { + try { + m_imp->prepareHeader(new char[bytePerSec], + bytePerSec, + m_imp->m_whdr[m_imp->m_index]); + m_imp->addBlock(m_imp->m_whdr[m_imp->m_index]); + } catch (TException &e) { + m_imp->m_isRecording = false; + for (int j = 0; j < (int)(m_imp->m_whdr.size() - 1); ++j) { + if (m_imp->m_whdr[j].dwFlags & WHDR_PREPARED) { + try { + m_imp->unprepareHeader(m_imp->m_whdr[j]); + } catch (TException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, e.getMessage()); + } + delete[] m_imp->m_whdr[j].lpData; + } else if (j == m_imp->m_index) + delete[] m_imp->m_whdr[j].lpData; + } + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, e.getMessage()); + } + } + + m_imp->m_executor.addTask(new RecordTask(m_imp)); +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackP &st, Source devtype) +{ + if (m_imp->m_isRecording) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Just another recoding is in progress"); + + m_imp->m_format = st->getFormat(); + /*if ((m_imp->m_format.m_bitPerSample == 8 && m_imp->m_format.m_signedSample) || + (m_imp->m_format.m_bitPerSample == 24)) + throw TException("This format is not supported for recording");*/ + try { + TSoundTrackFormat fmt = getPreferredFormat(st->getFormat()); + if (fmt != st->getFormat()) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + if (!setRecordLine(devtype)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Problems to set input source line to record"); + + m_imp->m_st = st; + m_imp->m_allocateBuff = false; + m_imp->m_isRecording = true; + ResetEvent(m_imp->m_hLastBlockDone); + ResetEvent(m_imp->m_hBlockDone); + m_imp->m_index = 0; + m_imp->m_recordedBlocks.clear(); + m_imp->m_byteRecorded = 0; + try { + WaveFormat wf(m_imp->m_format.m_channelCount, + m_imp->m_format.m_sampleRate, + m_imp->m_format.m_bitPerSample); + + m_imp->open(wf); + m_imp->prepareHeader((char *)st->getRawData(), + st->getSampleCount() * ((st->getBitPerSample() * st->getChannelCount()) >> 3), + m_imp->m_whdr[m_imp->m_index]); + m_imp->addBlock(m_imp->m_whdr[m_imp->m_index]); + } catch (TException &e) { + m_imp->m_isRecording = false; + if (m_imp->m_whdr[m_imp->m_index].dwFlags & WHDR_PREPARED) + m_imp->unprepareHeader(m_imp->m_whdr[m_imp->m_index]); + + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, e.getMessage()); + } + + m_imp->m_executor.addTask(new RecordTask(m_imp)); +} +#endif + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundInputDevice::stop() +{ + if (!m_imp->m_isRecording) + return 0; + + m_imp->m_isRecording = false; + try { + m_imp->reset(); + } catch (TException &e) { + for (int j = 0; j < (int)m_imp->m_whdr.size(); ++j) { + if (m_imp->m_whdr[j].dwFlags & WHDR_PREPARED) { + try { + m_imp->unprepareHeader(m_imp->m_whdr[j]); + } catch (TException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, e.getMessage()); + } + delete[] m_imp->m_whdr[j].lpData; + } + } + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, e.getMessage()); + } + + if (m_imp->m_allocateBuff) { + WaitForSingleObject(m_imp->m_hLastBlockDone, INFINITE); + + TSoundTrackP st = TSoundTrack::create( + m_imp->m_format, + m_imp->m_byteRecorded / ((m_imp->m_format.m_bitPerSample * m_imp->m_format.m_channelCount) >> 3)); + + TINT32 bytePerSec = m_imp->m_format.m_sampleRate * + ((m_imp->m_format.m_bitPerSample * m_imp->m_format.m_channelCount) >> 3); + + int i; + for (i = 0; i < (int)(m_imp->m_recordedBlocks.size() - 1); ++i) { + memcpy( + (void *)(st->getRawData() + bytePerSec * i), + m_imp->m_recordedBlocks[i], + bytePerSec); + delete[] m_imp->m_recordedBlocks[i]; + } + + TINT32 lastBlockSize = m_imp->m_byteRecorded - (bytePerSec * i); + + if (lastBlockSize != 0) { + memcpy((void *)(st->getRawData() + bytePerSec * i), + m_imp->m_recordedBlocks[i], + lastBlockSize); + delete[] m_imp->m_recordedBlocks[i]; + } + + try { + m_imp->close(); + } catch (TException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, e.getMessage()); + } + return st; + } else { + WaitForSingleObject(m_imp->m_hLastBlockDone, INFINITE); + try { + m_imp->close(); + } catch (TException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnableCloseDevice, e.getMessage()); + } + return m_imp->m_st; + } +} + +//------------------------------------------------------------------------------ +#ifndef TNZCORE_LIGHT + +void RecordTask::run() +{ + m_dev->start(); + + if (m_dev->m_allocateBuff) { + TINT32 bytePerSec = m_dev->m_format.m_sampleRate * + ((m_dev->m_format.m_bitPerSample * m_dev->m_format.m_channelCount) >> 3); + + while (m_dev->m_whdr[(m_dev->m_index + 1) % m_dev->m_whdr.size()].dwFlags & WHDR_PREPARED) { + if (m_dev->m_isRecording) + WaitForSingleObject(m_dev->m_hBlockDone, INFINITE); + int indexToPrepare = m_dev->m_index; + + // calcolo l'indice successivo per far l'unprepare + m_dev->m_index = (m_dev->m_index + 1) % m_dev->m_whdr.size(); + if (m_dev->m_whdr[m_dev->m_index].dwFlags & WHDR_DONE) { + TINT32 byteRecorded = m_dev->m_whdr[m_dev->m_index].dwBytesRecorded; + if (byteRecorded) { + m_dev->m_recordedBlocks.push_back(m_dev->m_whdr[m_dev->m_index].lpData); + m_dev->m_byteRecorded += byteRecorded; + } + + try { + m_dev->unprepareHeader(m_dev->m_whdr[m_dev->m_index]); + } catch (TException &) { + for (int i = 0; i < (int)m_dev->m_recordedBlocks.size(); ++i) + delete[] m_dev->m_recordedBlocks[i]; + return; + } + + if (byteRecorded == 0) { + delete[] m_dev->m_whdr[m_dev->m_index].lpData; + } + + // con questo controllo si evita che vengano accodati nuovi blocchi + // dopo che e' stata chiamata la waveInReset + if (m_dev->m_isRecording) { + try { + m_dev->prepareHeader(new char[bytePerSec], + bytePerSec, + m_dev->m_whdr[indexToPrepare]); + + m_dev->addBlock(m_dev->m_whdr[indexToPrepare]); + } catch (TException &) { + m_dev->m_isRecording = false; + for (int i = 0; i < (int)m_dev->m_recordedBlocks.size(); ++i) + delete[] m_dev->m_recordedBlocks[i]; + return; + } + } + + } else + m_dev->m_index = indexToPrepare; + } + } else { + if (m_dev->m_isRecording) + WaitForSingleObject(m_dev->m_hBlockDone, INFINITE); + try { + m_dev->unprepareHeader(m_dev->m_whdr[m_dev->m_index]); + m_dev->m_isRecording = false; + } catch (TException &) { + m_dev->m_isRecording = false; + return; + } + } + + SetEvent(m_dev->m_hLastBlockDone); + return; +} + +//------------------------------------------------------------------------------ +#endif + +double TSoundInputDevice::getVolume() +{ + DWORD dwVolumeControlID; + + UINT nNumMixers; + MMRESULT ret; + MIXERLINE mxl; + + nNumMixers = mixerGetNumDevs(); + + if (nNumMixers == 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Doesn't exist a audio mixer device"); + + // get dwLineID + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + // get dwControlID + MIXERCONTROL mxc; + ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); + if (ret == MIXERR_INVALCONTROL) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Is not possible to obtain info of volume by mixer"); + + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + dwVolumeControlID = mxc.dwControlID; + + MIXERCONTROLDETAILS_UNSIGNED mxcdVolume; + + ret = getControlDetails((HMIXEROBJ)0, + dwVolumeControlID, + mxc.cMultipleItems, + &mxcdVolume); + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + DWORD dwVal = mxcdVolume.dwValue; + + return (double)dwVal * 10.0 / (double)mxc.Bounds.dwMaximum; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::setVolume(double value) +{ + DWORD dwVolumeControlID, + dwMaximum; + + UINT nNumMixers; + MMRESULT ret; + MIXERLINE mxl; + + nNumMixers = mixerGetNumDevs(); + + if (nNumMixers == 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Doesn't exist a audio mixer device"); + + // get dwLineID + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + throw TException("Error to obtain info by mixer"); + + // get dwControlID + MIXERCONTROL mxc; + ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + dwMaximum = mxc.Bounds.dwMaximum; + dwVolumeControlID = mxc.dwControlID; + + int newValue; + double fattProp = ((double)(mxc.Metrics.cSteps - 1) * value) / 10; + double delta = (double)(dwMaximum / (mxc.Metrics.cSteps - 1)); + newValue = (int)(tround(fattProp) * delta); + + MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = {newValue}; + ret = setControlDetails((HMIXEROBJ)0, + dwVolumeControlID, + mxc.cMultipleItems, + &mxcdVolume); + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::supportsVolume() +{ + UINT nNumMixers; + MMRESULT ret; + MIXERLINE mxl; + + nNumMixers = mixerGetNumDevs(); + + if (nNumMixers == 0) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "Doesn't exist a audio mixer device"); + + // get dwLineID + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + // get dwControlID + MIXERCONTROL mxc; + ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); + if (ret == MIXERR_INVALCONTROL) + return false; + + if (ret != MMSYSERR_NOERROR) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "Error to obtain info by mixer"); + + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::isRecording() +{ + return m_imp->m_isRecording; +} + +//------------------------------------------------------------------------------ + +/*TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + ULONG sampleRate, int channelCount, int bitPerSample) +{ + MMRESULT ret; + TSoundTrackFormat fmt; + + ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); + + if (ret == MMSYSERR_NOERROR) + { + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + return fmt; + } + if (ret == WAVERR_BADFORMAT) + { + //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini + std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); + if (it == m_imp->m_supportedRate.end()) + { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported format"); + } + else + sampleRate = *it; + ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); + if (ret == MMSYSERR_NOERROR) + { + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + return fmt; + } + if (ret == WAVERR_BADFORMAT) + { + //cambiare bps + if (bitPerSample <= 8) + bitPerSample = 8; + else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) + bitPerSample = 16; + + ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); + if (ret == MMSYSERR_NOERROR) + { + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + return fmt; + } + if (ret == WAVERR_BADFORMAT) + { + //switch mono/stereo + if (channelCount <= 1) + channelCount = 1; + else + channelCount = 2; + + ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); + if (ret == MMSYSERR_NOERROR) + { + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + return fmt; + } + if (ret == WAVERR_BADFORMAT) + { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Doesn't exist a preferred format"); + } + } + } + } + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Error to query supported format"); +} +*/ +TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + TUINT32 sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + + //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini + std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported format"); + } else + sampleRate = *it; + + if (bitPerSample <= 8) + bitPerSample = 8; + else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) + bitPerSample = 16; + + if (bitPerSample >= 16) + fmt.m_signedSample = true; + else + fmt.m_signedSample = false; + + //switch mono/stereo + if (channelCount <= 1) + channelCount = 1; + else + channelCount = 2; + + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } +} + +//============================================================================== +//============================================================================== +// Funzioni per l'interazione con il mixer device +//============================================================================== +//============================================================================== + +namespace +{ + +// restituisce dentro la struttura mxc le informazioni relative +// al controllo di tipo dwControlType associato alla linea +// identificata da dwLineID +MMRESULT getLineControl(MIXERCONTROL &mxc, + HMIXEROBJ hMixer, + DWORD dwLineID, + DWORD dwControlType) +{ + MIXERLINECONTROLS mxlc; + mxlc.cbStruct = sizeof(MIXERLINECONTROLS); + mxlc.dwLineID = dwLineID; + mxlc.dwControlType = dwControlType; + mxlc.cControls = 1; + mxlc.cbmxctrl = sizeof(MIXERCONTROL); + mxlc.pamxctrl = &mxc; + MMRESULT ret = mixerGetLineControls((HMIXEROBJ)hMixer, + &mxlc, + MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE); + return ret; +} + +//------------------------------------------------------------------------------ + +// restituisce nella struttura mxl le informazioni relative alla linea +// sorgente individuata dagli estremi destination e source +MMRESULT getLineInfo(HMIXEROBJ hMixer, + MIXERLINE &mxl, + DWORD destination, + DWORD source) +{ + MMRESULT ret; + + mxl.cbStruct = sizeof(mxl); + mxl.dwDestination = destination; + mxl.dwSource = source; + ret = mixerGetLineInfo(0, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_SOURCE); + return ret; +} + +//------------------------------------------------------------------------------ + +// restituisce nella struttura mxl le informazioni relative alla linea +// individuata da dwLineID +MMRESULT getLineInfo(HMIXEROBJ hMixer, + MIXERLINE &mxl, + DWORD dwLineID) +{ + MMRESULT ret; + + mxl.cbStruct = sizeof(mxl); + mxl.dwLineID = dwLineID; + ret = mixerGetLineInfo((HMIXEROBJ)hMixer, + &mxl, + MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID); + return ret; +} + +//------------------------------------------------------------------------------ + +// restituisce nella struttura mxl le informazioni relative alla linea +// individuata dal tipo specificato in dwComponentType +MMRESULT getLineInfo(HMIXEROBJ hMixer, + DWORD dwComponentType, + MIXERLINE &mxl) +{ + MMRESULT ret; + + mxl.cbStruct = sizeof(MIXERLINE); + mxl.dwComponentType = dwComponentType; + ret = mixerGetLineInfo((HMIXEROBJ)hMixer, + &mxl, + MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE); + return ret; +} + +//------------------------------------------------------------------------------ + +// consente di settare il valore booleano specificato in mxcdSelectValue +// relativo al controllo specificato in dwSelectControlID +MMRESULT setControlDetails(HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_BOOLEAN *mxcdSelectValue) +{ + MMRESULT ret; + MIXERCONTROLDETAILS mxcd; + mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); + mxcd.dwControlID = dwSelectControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = dwMultipleItems; + mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); + mxcd.paDetails = mxcdSelectValue; + ret = mixerSetControlDetails((HMIXEROBJ)hMixer, + &mxcd, + MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); + return ret; +} + +//------------------------------------------------------------------------------ + +// consente di settare il valore UNSIGNED specificato in mxcdSelectValue +// relativo al controllo specificato in dwSelectControlID +MMRESULT setControlDetails(HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue) +{ + MMRESULT ret; + MIXERCONTROLDETAILS mxcd; + mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); + mxcd.dwControlID = dwSelectControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = dwMultipleItems; + mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); + mxcd.paDetails = mxcdSelectValue; + ret = mixerSetControlDetails((HMIXEROBJ)hMixer, + &mxcd, + MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); + return ret; +} + +//------------------------------------------------------------------------------ + +// consente di ottenere il valore UNSIGNED specificato in mxcdSelectValue +// relativo al controllo specificato in dwSelectControlID +MMRESULT getControlDetails(HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue) +{ + MMRESULT ret; + MIXERCONTROLDETAILS mxcd; + mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); + mxcd.dwControlID = dwSelectControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = dwMultipleItems; + mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); + mxcd.paDetails = mxcdSelectValue; + ret = mixerGetControlDetails((HMIXEROBJ)hMixer, + &mxcd, + MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); + return ret; +} +//------------------------------------------------------------------------------ + +// consente di ottenere la lista di informazioni in pmxcdSelectText +// relativo al controllo specificato in dwSelectControlID +MMRESULT getControlDetails(HMIXEROBJ hMixer, + DWORD dwSelectControlID, + DWORD dwMultipleItems, + MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText) +{ + MMRESULT ret; + MIXERCONTROLDETAILS mxcd; + + mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); + mxcd.dwControlID = dwSelectControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = dwMultipleItems; + mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); + mxcd.paDetails = pmxcdSelectText; + ret = mixerGetControlDetails((HMIXEROBJ)0, + &mxcd, + MIXER_GETCONTROLDETAILSF_LISTTEXT); + return ret; +} + +//------------------------------------------------------------------------------ + +// restituiscei l nome della linea identificata da lineID +string getMixerLineName(DWORD lineID) +{ + MIXERLINE mxl; + MMRESULT ret; + + ret = getLineInfo((HMIXEROBJ)0, mxl, lineID); +#ifdef TNZCORE_LIGHT + assert(false); + return ""; +#else + return string(mxl.szName); +#endif +} + +//------------------------------------------------------------------------------ + +// restituisce la lista degli identificativi delle linee sorgente associate +// alla destinazione di tipo dstComponentType +list getMixerSrcLines(DWORD dstComponentType) +{ + list srcList; + MMRESULT ret; + MIXERLINE mxl; + + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + //forse bisognerebbe lanciare un'eccezione + return srcList; //non ha linea di dst per la registrazione + + int v; + for (v = 0; v < (int)mxl.cConnections; v++) { + MIXERLINE mxl1; + + ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); + if (ret == MMSYSERR_NOERROR) + srcList.push_back(mxl1.dwLineID); + } + return srcList; +} + +//------------------------------------------------------------------------------ + +// restituisce la lista degli identificativi delle linee sorgente di tipo +// srcComponentType associate alla destinazione di tipo dstComponentType +list getMixerSrcLines(DWORD dstComponentType, DWORD srcComponentType) +{ + list srcList; + MMRESULT ret; + MIXERLINE mxl; + + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + //forse bisognerebbe lanciare un'eccezione + return srcList; //non ha linea di dst per la registrazione + + int v; + for (v = 0; v < (int)mxl.cConnections; v++) { + MIXERLINE mxl1; + + ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); + if (ret == MMSYSERR_NOERROR) + if (mxl1.dwComponentType == srcComponentType) + srcList.push_back(mxl1.dwLineID); + } + return srcList; +} + +//------------------------------------------------------------------------------ + +// restituisce true sse la linea destinazione di tipo dstComponentType +// supporta una linea sorgente di tipo srcComponentType +bool isSrcLineSupported(DWORD dstComponentType, DWORD srcComponentType) +{ + // ci possono essere piu' linee sorgente dello stesso tipo in + // corrispondenza di una data linea destinazione ? + MMRESULT ret; + MIXERLINE mxl; + + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + return false; //non ha linea di dst per la registrazione + + int v; + for (v = 0; v < (int)mxl.cConnections; v++) { + MIXERLINE mxl1; + + ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); + if (ret == MMSYSERR_NOERROR) + if (mxl1.dwComponentType == srcComponentType) + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ + +bool activateSrcLine(const MIXERLINE &mxlDst, DWORD componentTypeSrc) +{ + if (!isSrcLineSupported(mxlDst.dwComponentType, componentTypeSrc)) + return false; + + bool bRetVal = true; + + for (DWORD v = 0; v < mxlDst.cConnections; v++) { + MIXERLINE mxlSrc; + MMRESULT ret = getLineInfo((HMIXEROBJ)0, mxlSrc, mxlDst.dwDestination, v); + + if (ret == MMSYSERR_NOERROR) { + // chiedo il controllo di tipo MUTE della linea sorgente + MIXERCONTROL mxc; + ret = getLineControl( + mxc, (HMIXEROBJ)0, mxlSrc.dwLineID, + MIXERCONTROL_CONTROLTYPE_MUTE); + + if (ret == MMSYSERR_NOERROR) { + MIXERCONTROLDETAILS_BOOLEAN mxcdSelectValue; + mxcdSelectValue.fValue = mxlSrc.dwComponentType == componentTypeSrc ? 0L : 1L; + + ret = setControlDetails((HMIXEROBJ)0, mxc.dwControlID, + mxc.cMultipleItems, &mxcdSelectValue); + if (ret != MMSYSERR_NOERROR) + bRetVal = false; + } + } + } + return bRetVal; +} + +//------------------------------------------------------------------------------ + +bool setSrcMixMuxControl(MIXERCONTROL mxc, DWORD componentTypeSrc) +{ + MMRESULT ret; + DWORD dwIndexLine; + bool found = false; + + // mantengo nota del ID del controllo dsst individuato e + // del numero di linee src ad esso associate + DWORD dwSelectControlID = mxc.dwControlID; + DWORD dwMultipleItems = mxc.cMultipleItems; + + if (dwMultipleItems == 0) + return false; + + // determino l'indice dell'item corrispondente alla linea sorgente + // di tipo componentTypeSrc + + MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText = + new MIXERCONTROLDETAILS_LISTTEXT[dwMultipleItems]; + + if (pmxcdSelectText != NULL) { + // estraggo le info su tutte le linee associate al controllo + ret = getControlDetails((HMIXEROBJ)0, dwSelectControlID, + dwMultipleItems, pmxcdSelectText); + + if (ret == MMSYSERR_NOERROR) { + for (DWORD dwi = 0; dwi < dwMultipleItems; dwi++) { + // prendo le info su ogni linea e verifico se e' del giusto tipo + MIXERLINE mxl; + ret = getLineInfo((HMIXEROBJ)0, mxl, pmxcdSelectText[dwi].dwParam1); + if (ret == MMSYSERR_NOERROR && mxl.dwComponentType == componentTypeSrc) { + dwIndexLine = dwi; + found = true; + break; + } + } + } + + delete[] pmxcdSelectText; + + if (!found) + return false; + } + + if (dwIndexLine >= dwMultipleItems) + return false; + + bool bRetVal = false; + + MIXERCONTROLDETAILS_BOOLEAN *pmxcdSelectValue = + new MIXERCONTROLDETAILS_BOOLEAN[dwMultipleItems]; + + if (pmxcdSelectValue != NULL) { + ::ZeroMemory(pmxcdSelectValue, dwMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); + + // impostazione del valore + pmxcdSelectValue[dwIndexLine].fValue = (TINT32)1; // lVal; //dovrebbe esser uno + + ret = setControlDetails((HMIXEROBJ)0, + dwSelectControlID, + dwMultipleItems, + pmxcdSelectValue); + if (ret == MMSYSERR_NOERROR) + bRetVal = true; + + delete[] pmxcdSelectValue; + } + return bRetVal; +} + +//------------------------------------------------------------------------------ + +bool setRecordLine(TSoundInputDevice::Source typeInput) +{ + DWORD dwComponentTypeSrc; + + UINT nNumMixers; + MMRESULT ret; + MIXERLINE mxl = {0}; + + switch (typeInput) { + case TSoundInputDevice::LineIn: + dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_LINE /*| + MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY | + MIXERLINE_COMPONENTTYPE_SRC_ANALOG*/; + break; + case TSoundInputDevice::DigitalIn: + dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL; + break; + case TSoundInputDevice::CdAudio: + dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; + break; + default: + dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; + } + + nNumMixers = mixerGetNumDevs(); + + if (nNumMixers == 0) + return false; + + // utilizziamo il MIXER di default identificato dall'indice 0 + // vedo se il device ha una linea dst per il wave_input + ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); + if (ret != MMSYSERR_NOERROR) + return false; //non ha linea di dst per la registrazione + + //vediamo che tipo controllo ha questa linea dst + + // sara' un MIXER? + MIXERCONTROL mxc = {0}; + + ret = getLineControl(mxc, + (HMIXEROBJ)0, + mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_MIXER); + + if (ret != MMSYSERR_NOERROR) { + // no mixer, try MUX + ret = getLineControl(mxc, + (HMIXEROBJ)0, + mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_MUX); + + if (ret != MMSYSERR_NOERROR) { + // vediamo se e' uno di quei device ne' MIXER ne' MUX + return activateSrcLine(mxl, dwComponentTypeSrc); + } else { + // la linea ha un controllo di tipo MUX + return setSrcMixMuxControl(mxc, dwComponentTypeSrc); + } + } else { + // la linea ha un controllo di tipo MIXER + return setSrcMixMuxControl(mxc, dwComponentTypeSrc); + } +} + +//------------------------------------------------------------------------------ + +MMRESULT isaFormatSupported( + int sampleRate, int channelCount, int bitPerSample, bool input) +{ + WAVEFORMATEX wf; + MMRESULT ret; + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = channelCount; + wf.nSamplesPerSec = sampleRate; + wf.wBitsPerSample = bitPerSample; + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = 0; + + if (input) + ret = waveInOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); + else + ret = waveOutOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); + return ret; +} +} diff --git a/toonz/sources/common/tsound/tsound_x.cpp b/toonz/sources/common/tsound/tsound_x.cpp new file mode 100644 index 0000000..b86e1ae --- /dev/null +++ b/toonz/sources/common/tsound/tsound_x.cpp @@ -0,0 +1,1233 @@ + + +#include "tsound_t.h" +#include "texception.h" +#include "tthread.h" + +#include +#include +#include +#include +#include + +//forward declaration +namespace +{ +bool isInterfaceSupported(int deviceType, int interfaceType); +bool setDefaultInput(TSoundInputDevice::Source type); +bool setDefaultOutput(); +bool isChangeOutput(ULONG sampleRate); +} +//============================================================================== + +class TSoundOutputDeviceImp +{ +public: + ALport m_port; + bool m_stopped; + bool m_isPlaying; + bool m_looped; + TSoundTrackFormat m_currentFormat; + std::queue m_queuedSoundTracks; + std::set m_supportedRate; + + TThread::Executor m_executor; + TThread::Mutex m_mutex; + + TSoundOutputDeviceImp() + : m_stopped(false), m_isPlaying(false), m_looped(false), m_port(NULL), m_queuedSoundTracks(), m_supportedRate(){}; + + ~TSoundOutputDeviceImp(){}; + + bool doOpenDevice(const TSoundTrackFormat &format); + void insertAllRate(); + bool verifyRate(); +}; + +//----------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) +{ + ALconfig config; + ALpv pvbuf[3]; + + m_currentFormat = format; + + // AL_MONITOR_CTL fa parte dei vecchi andrebbero trovati quelli nuovi + pvbuf[0].param = AL_PORT_COUNT; + pvbuf[1].param = AL_MONITOR_CTL; + if (alGetParams(AL_DEFAULT_OUTPUT, pvbuf, 2) < 0) + if (oserror() == AL_BAD_DEVICE_ACCESS) + return false; //throw TException("Could not access audio hardware."); + + if (!isInterfaceSupported(AL_DEFAULT_OUTPUT, AL_SPEAKER_IF_TYPE)) + return false; //throw TException("Speakers are not supported"); + + int dev = alGetResourceByName(AL_SYSTEM, (char *)"Headphone/Speaker", AL_DEVICE_TYPE); + if (!dev) + return false; //throw TException("invalid device speakers"); + + pvbuf[0].param = AL_DEFAULT_OUTPUT; + pvbuf[0].value.i = dev; + alSetParams(AL_SYSTEM, pvbuf, 1); + + ALfixed buf[2] = {alDoubleToFixed(0), alDoubleToFixed(0)}; + + config = alNewConfig(); + // qui devo metterci gli altoparlanti e poi setto i valori per il default output + pvbuf[0].param = AL_RATE; + pvbuf[0].value.ll = alDoubleToFixed((double)format.m_sampleRate); + pvbuf[1].param = AL_GAIN; + pvbuf[1].value.ptr = buf; + pvbuf[1].sizeIn = 8; + pvbuf[2].param = AL_INTERFACE; + pvbuf[2].value.i = AL_SPEAKER_IF_TYPE; + + if (alSetParams(AL_DEFAULT_OUTPUT, pvbuf, 3) < 0) + return false; + //throw TException("Unable to set params for output device"); + + if (alSetChannels(config, format.m_channelCount) == -1) + return false; //throw TException("Error to setting audio hardware."); + + int bytePerSample = format.m_bitPerSample >> 3; + switch (bytePerSample) { + case 3: + bytePerSample++; + break; + default: + break; + } + + if (alSetWidth(config, bytePerSample) == -1) + return false; //throw TException("Error to setting audio hardware."); + + if (alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP) == -1) + return false; //throw TException("Error to setting audio hardware."); + + if (alSetQueueSize(config, (TINT32)format.m_sampleRate) == -1) + return false; //throw TException("Error to setting audio hardware."); + + m_port = alOpenPort("AudioOutput", "w", config); + if (!m_port) + return false; //throw TException("Could not open audio port."); + + alFreeConfig(config); + return true; +} + +//----------------------------------------------------------------------------- + +void TSoundOutputDeviceImp::insertAllRate() +{ + m_supportedRate.insert(8000); + m_supportedRate.insert(11025); + m_supportedRate.insert(16000); + m_supportedRate.insert(22050); + m_supportedRate.insert(32000); + m_supportedRate.insert(44100); + m_supportedRate.insert(48000); +} + +//----------------------------------------------------------------------------- + +bool TSoundOutputDeviceImp::verifyRate() +{ + //Sample Rate + ALparamInfo pinfo; + int ret = alGetParamInfo(AL_DEFAULT_OUTPUT, AL_RATE, &pinfo); + if (ret != -1 && pinfo.elementType == AL_FIXED_ELEM) { + int min = (int)alFixedToDouble(pinfo.min.ll); + int max = (int)alFixedToDouble(pinfo.max.ll); + + std::set::iterator it; + for (it = m_supportedRate.begin(); it != m_supportedRate.end(); ++it) + if (*it < min || *it > max) + m_supportedRate.erase(*it); + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + } else if (ret == AL_BAD_PARAM) + return false; + else + return false; + return true; +} + +//============================================================================== + +class PlayTask : public TThread::Runnable +{ +public: + TSoundOutputDeviceImp *m_devImp; + TSoundTrackP m_sndtrack; + + PlayTask(TSoundOutputDeviceImp *devImp, const TSoundTrackP &st) + : TThread::Runnable(), m_devImp(devImp), m_sndtrack(st){}; + + ~PlayTask(){}; + + void run(); +}; + +void PlayTask::run() +{ + int leftToPlay = m_sndtrack->getSampleCount(); + int i = 0; + + if (!m_devImp->m_port || + (m_devImp->m_currentFormat != m_sndtrack->getFormat()) || + isChangeOutput(m_sndtrack->getSampleRate())) + if (!m_devImp->doOpenDevice(m_sndtrack->getFormat())) + return; + + while ((leftToPlay > 0) && m_devImp->m_isPlaying) { + int fillable = alGetFillable(m_devImp->m_port); + if (!fillable) + continue; + + if (fillable < leftToPlay) { + alWriteFrames(m_devImp->m_port, (void *)(m_sndtrack->getRawData() + i), fillable); + // ricorda getSampleSize restituisce m_sampleSize che comprende gia' + // la moltiplicazione per il numero dei canali + i += fillable * m_sndtrack->getSampleSize(); + leftToPlay -= fillable; + } else { + alWriteFrames(m_devImp->m_port, (void *)(m_sndtrack->getRawData() + i), leftToPlay); + leftToPlay = 0; + } + } + + if (!m_devImp->m_stopped) { + while (ALgetfilled(m_devImp->m_port) > 0) + sginap(1); + { + TThread::ScopedLock sl(m_devImp->m_mutex); + if (!m_devImp->m_looped) + m_devImp->m_queuedSoundTracks.pop(); + if (m_devImp->m_queuedSoundTracks.empty()) { + m_devImp->m_isPlaying = false; + m_devImp->m_stopped = true; + m_devImp->m_looped = false; + } else { + m_devImp->m_executor.addTask( + new PlayTask(m_devImp, m_devImp->m_queuedSoundTracks.front())); + } + } + } else { + alDiscardFrames(m_devImp->m_port, alGetFilled(m_devImp->m_port)); + while (!m_devImp->m_queuedSoundTracks.empty()) + m_devImp->m_queuedSoundTracks.pop(); + } +} + +//============================================================================== + +TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp) +{ + if (!setDefaultOutput()) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, "Speaker not supported"); + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + m_imp->insertAllRate(); +} + +//------------------------------------------------------------------------------ + +TSoundOutputDevice::~TSoundOutputDevice() +{ + close(); + delete m_imp; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::installed() +{ + if (alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, 0, 0, 0, 0) <= 0) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::open(const TSoundTrackP &st) +{ + if (!m_imp->doOpenDevice(st->getFormat())) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Problem to open the output device or set some params"); + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::close() +{ + stop(); + if (m_imp->m_port) + alClosePort(m_imp->m_port); + m_imp->m_port = NULL; + + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) +{ + if (!st->getSampleCount()) + return; + + { + TThread::ScopedLock sl(m_imp->m_mutex); + if (m_imp->m_looped) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Unable to queue another playback when the sound player is looping"); + + m_imp->m_isPlaying = true; + m_imp->m_stopped = false; + m_imp->m_looped = loop; + } + + TSoundTrackFormat fmt; + try { + fmt = getPreferredFormat(st->getFormat()); + if (fmt != st->getFormat()) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + assert(s1 >= s0); + TSoundTrackP subTrack = st->extract(s0, s1); + + //far partire il thread + if (m_imp->m_queuedSoundTracks.empty()) { + m_imp->m_queuedSoundTracks.push(subTrack); + + m_imp->m_executor.addTask(new PlayTask(m_imp, subTrack)); + } else + m_imp->m_queuedSoundTracks.push(subTrack); +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::stop() +{ + if (!m_imp->m_isPlaying) + return; + + TThread::ScopedLock sl(m_imp->m_mutex); + m_imp->m_isPlaying = false; + m_imp->m_stopped = true; + m_imp->m_looped = false; +} + +//------------------------------------------------------------------------------ + +double TSoundOutputDevice::getVolume() +{ + ALpv pv[1]; + ALfixed value[2]; + + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + pv[0].param = AL_GAIN; + pv[0].value.ptr = value; + pv[0].sizeIn = 8; + alGetParams(AL_DEFAULT_OUTPUT, pv, 1); + + double val = (((alFixedToDouble(value[0]) + alFixedToDouble(value[1])) / 2.) + 60.) / 8.05; + return val; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::setVolume(double volume) +{ + ALpv pv[1]; + ALfixed value[2]; + + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + double val = -60. + 8.05 * volume; + value[0] = alDoubleToFixed(val); + value[1] = alDoubleToFixed(val); + + pv[0].param = AL_GAIN; + pv[0].value.ptr = value; + pv[0].sizeIn = 8; + if (alSetParams(AL_DEFAULT_OUTPUT, pv, 1) < 0) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::supportsVolume() +{ + ALparamInfo pinfo; + int ret; + ret = alGetParamInfo(AL_DEFAULT_OUTPUT, AL_GAIN, &pinfo); + double min = alFixedToDouble(pinfo.min.ll); + double max = alFixedToDouble(pinfo.max.ll); + if ((ret != -1) && (min != max) && (max != 0.0)) + return true; + else if ((ret == AL_BAD_PARAM) || ((min == max) && (max == 0.0))) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "It is impossible to chamge setting of volume"); + else + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, "Output device is not accessible"); +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isPlaying() const +{ + return m_imp->m_isPlaying; +} + +//------------------------------------------------------------------------------ + +bool TSoundOutputDevice::isLooping() +{ + TThread::ScopedLock sl(m_imp->m_mutex); + return m_imp->m_looped; +} + +//------------------------------------------------------------------------------ + +void TSoundOutputDevice::setLooping(bool loop) +{ + TThread::ScopedLock sl(m_imp->m_mutex); + m_imp->m_looped = loop; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( + ULONG sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + int ret; + + if (!m_imp->verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't any support rate"); + + if (m_imp->m_supportedRate.find((int)sampleRate) == + m_imp->m_supportedRate.end()) { + std::set::iterator it = + m_imp->m_supportedRate.lower_bound((int)sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported rate"); + } else + sampleRate = *it; + } + + int value; + ALvalue vals[32]; + if ((ret = alQueryValues(AL_DEFAULT_OUTPUT, AL_CHANNELS, vals, 32, 0, 0)) > 0) + for (int i = 0; i < ret; ++i) + value = vals[i].i; + else if (oserror() == AL_BAD_PARAM) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "It is impossible ask for the max numbers of channels supported"); + else + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "It is impossibile information about ouput device"); + + if (value > 2) + value = 2; + if (channelCount > value) + channelCount = value; + else if (channelCount <= 0) + channelCount = 1; + + if (bitPerSample <= 8) + bitPerSample = 8; + else if (bitPerSample <= 16) + bitPerSample = 16; + else + bitPerSample = 24; + + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + fmt.m_signedSample = true; + + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } +} + +//============================================================================== +//============================================================================== +// REGISTRAZIONE +//============================================================================== +//============================================================================== + +class TSoundInputDeviceImp +{ +public: + ALport m_port; + bool m_stopped; + bool m_isRecording; + bool m_oneShotRecording; + + TINT32 m_recordedSampleCount; + + vector m_recordedBlocks; + vector m_samplePerBlocks; + TSoundTrackFormat m_currentFormat; + TSoundTrackP m_st; + std::set m_supportedRate; + + TThread::Executor m_executor; + + TSoundInputDeviceImp() + : m_stopped(false), m_isRecording(false), m_port(NULL), m_recordedBlocks(), m_samplePerBlocks(), m_recordedSampleCount(0), m_oneShotRecording(false), m_st(0), m_supportedRate(){}; + + ~TSoundInputDeviceImp(){}; + + bool doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType); + void insertAllRate(); + bool verifyRate(); +}; + +bool TSoundInputDeviceImp::doOpenDevice(const TSoundTrackFormat &format, + TSoundInputDevice::Source devType) +{ + ALconfig config; + ALpv pvbuf[2]; + + m_currentFormat = format; + + // AL_MONITOR_CTL fa parte dei vecchi andrebbero trovati quelli nuovi + pvbuf[0].param = AL_PORT_COUNT; + pvbuf[1].param = AL_MONITOR_CTL; + if (alGetParams(AL_DEFAULT_INPUT, pvbuf, 2) < 0) + if (oserror() == AL_BAD_DEVICE_ACCESS) + return false; //throw TException("Could not access audio hardware."); + + config = alNewConfig(); + + if (!setDefaultInput(devType)) + return false; //throw TException("Could not set the input device specified"); + + pvbuf[0].param = AL_RATE; + pvbuf[0].value.ll = alDoubleToFixed(format.m_sampleRate); + + ALfixed buf[2] = {alDoubleToFixed(0), alDoubleToFixed(0)}; + pvbuf[1].param = AL_GAIN; + pvbuf[1].value.ptr = buf; + pvbuf[1].sizeIn = 8; + + if (alSetParams(AL_DEFAULT_INPUT, pvbuf, 2) < 0) + return false; //throw TException("Problem to set params "); + + if (alSetChannels(config, format.m_channelCount) == -1) + return false; //throw TException("Error to setting audio hardware."); + + int bytePerSample = format.m_bitPerSample >> 3; + switch (bytePerSample) { + case 3: + bytePerSample++; + break; + default: + break; + } + if (alSetWidth(config, bytePerSample) == -1) + return false; //throw TException("Error to setting audio hardware."); + + if (alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP) == -1) + return false; //throw TException("Error to setting audio hardware."); + + if (alSetQueueSize(config, (TINT32)format.m_sampleRate) == -1) + return false; //throw TException("Error to setting audio hardware."); + + alSetDevice(config, AL_DEFAULT_INPUT); + + m_port = alOpenPort("AudioInput", "r", config); + if (!m_port) + return false; //throw TException("Could not open audio port."); + + alFreeConfig(config); + return true; +} + +//----------------------------------------------------------------------------- + +void TSoundInputDeviceImp::insertAllRate() +{ + m_supportedRate.insert(8000); + m_supportedRate.insert(11025); + m_supportedRate.insert(16000); + m_supportedRate.insert(22050); + m_supportedRate.insert(32000); + m_supportedRate.insert(44100); + m_supportedRate.insert(48000); +} + +//----------------------------------------------------------------------------- + +bool TSoundInputDeviceImp::verifyRate() +{ + //Sample Rate + ALparamInfo pinfo; + int ret = alGetParamInfo(AL_DEFAULT_INPUT, AL_RATE, &pinfo); + if (ret != -1 && pinfo.elementType == AL_FIXED_ELEM) { + int min = (int)alFixedToDouble(pinfo.min.ll); + int max = (int)alFixedToDouble(pinfo.max.ll); + + std::set::iterator it; + for (it = m_supportedRate.begin(); it != m_supportedRate.end(); ++it) + if (*it < min || *it > max) + m_supportedRate.erase(*it); + if (m_supportedRate.end() == m_supportedRate.begin()) + return false; + } else if (ret == AL_BAD_PARAM) + return false; + else + return false; + return true; +} + +//============================================================================== + +class RecordTask : public TThread::Runnable +{ +public: + TSoundInputDeviceImp *m_devImp; + int m_ByteToSample; + + RecordTask(TSoundInputDeviceImp *devImp, int numByte) + : TThread::Runnable(), m_devImp(devImp), m_ByteToSample(numByte){}; + + ~RecordTask(){}; + + void run(); +}; + +void RecordTask::run() +{ + TINT32 byteRecordedSample = 0; + int filled = alGetFilled(m_devImp->m_port); + + if (m_devImp->m_oneShotRecording) { + char *rawData = m_devImp->m_recordedBlocks.front(); + int sampleSize; + + if ((m_devImp->m_currentFormat.m_bitPerSample >> 3) == 3) + sampleSize = 4; + else + sampleSize = (m_devImp->m_currentFormat.m_bitPerSample >> 3); + + sampleSize *= m_devImp->m_currentFormat.m_channelCount; + while ((byteRecordedSample <= (m_ByteToSample - filled * sampleSize)) && m_devImp->m_isRecording) { + alReadFrames(m_devImp->m_port, (void *)(rawData + byteRecordedSample), filled); + byteRecordedSample += filled * sampleSize; + filled = alGetFilled(m_devImp->m_port); + } + + if (m_devImp->m_isRecording) { + alReadFrames(m_devImp->m_port, (void *)(rawData + byteRecordedSample), + (m_ByteToSample - byteRecordedSample) / sampleSize); + while (alGetFillable(m_devImp->m_port) > 0) + sginap(1); + } + } else { + while (m_devImp->m_isRecording) { + filled = alGetFilled(m_devImp->m_port); + if (filled > 0) { + char *dataBuffer = new char[filled * m_ByteToSample]; + m_devImp->m_recordedBlocks.push_back(dataBuffer); + m_devImp->m_samplePerBlocks.push_back(filled * m_ByteToSample); + + alReadFrames(m_devImp->m_port, (void *)dataBuffer, filled); + m_devImp->m_recordedSampleCount += filled; + } + } + while (alGetFillable(m_devImp->m_port) > 0) + sginap(1); + } + alClosePort(m_devImp->m_port); + m_devImp->m_port = 0; + m_devImp->m_stopped = true; +} + +//============================================================================== + +TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp) +{ +} + +//------------------------------------------------------------------------------ + +TSoundInputDevice::~TSoundInputDevice() +{ + if (m_imp->m_port) + alClosePort(m_imp->m_port); + delete m_imp; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::installed() +{ + if (alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, 0, 0, 0, 0) <= 0) + return false; + return true; +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackFormat &format, TSoundInputDevice::Source type) +{ + if (m_imp->m_isRecording == true) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Just another recoding is in progress"); + + m_imp->m_recordedBlocks.clear(); + m_imp->m_samplePerBlocks.clear(); + + //registra creando una nuova traccia + m_imp->m_oneShotRecording = false; + + if (!setDefaultInput(type)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Error to set the input device"); + + m_imp->insertAllRate(); + TSoundTrackFormat fmt; + + try { + fmt = getPreferredFormat(format); + if (fmt != format) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + if (!m_imp->m_port) + m_imp->doOpenDevice(format, type); + + m_imp->m_currentFormat = format; + m_imp->m_isRecording = true; + m_imp->m_stopped = false; + m_imp->m_recordedSampleCount = 0; + + int bytePerSample = format.m_bitPerSample >> 3; + switch (bytePerSample) { + case 3: + bytePerSample++; + break; + default: + break; + } + bytePerSample *= format.m_channelCount; + + //far partire il thread + /*TRecordThread *recordThread = new TRecordThread(m_imp, bytePerSample); + if (!recordThread) + { + m_imp->m_isRecording = false; + m_imp->m_stopped = true; + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Unable to create the recording thread"); + } + recordThread->start();*/ + m_imp->m_executor.addTask(new RecordTask(m_imp, bytePerSample)); +} + +//------------------------------------------------------------------------------ + +void TSoundInputDevice::record(const TSoundTrackP &st, TSoundInputDevice::Source type) +{ + if (m_imp->m_isRecording == true) + throw TSoundDeviceException( + TSoundDeviceException::Busy, + "Just another recoding is in progress"); + + m_imp->m_recordedBlocks.clear(); + m_imp->m_samplePerBlocks.clear(); + + if (!setDefaultInput(type)) + throw TSoundDeviceException( + TSoundDeviceException::UnableSetDevice, + "Error to set the input device"); + + m_imp->insertAllRate(); + TSoundTrackFormat fmt; + + try { + fmt = getPreferredFormat(st->getFormat()); + if (fmt != st->getFormat()) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "Unsupported Format"); + } + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + e.getMessage()); + } + + if (!m_imp->m_port) + if (!m_imp->doOpenDevice(st->getFormat(), type)) + throw TSoundDeviceException( + TSoundDeviceException::UnableOpenDevice, + "Unable to open input device"); + + //Sovrascive un'intera o parte di traccia gia' esistente + m_imp->m_oneShotRecording = true; + m_imp->m_currentFormat = st->getFormat(); + m_imp->m_isRecording = true; + m_imp->m_stopped = false; + m_imp->m_recordedSampleCount = 0; + m_imp->m_st = st; + + m_imp->m_recordedBlocks.push_back((char *)st->getRawData()); + + int totByteToSample = st->getSampleCount() * st->getSampleSize(); + + //far partire il thread + /*TRecordThread *recordThread = new TRecordThread(m_imp, totByteToSample); + if (!recordThread) + { + m_imp->m_isRecording = false; + m_imp->m_stopped = true; + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "Unable to create the recording thread"); + } + recordThread->start();*/ + m_imp->m_executor.addTask(new RecordTask(m_imp, totByteToSample)); +} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundInputDevice::stop() +{ + TSoundTrackP st; + + if (!m_imp->m_isRecording) + throw TSoundDeviceException( + TSoundDeviceException::UnablePrepare, + "No recording process is in execution"); + + m_imp->m_isRecording = false; + + alDiscardFrames(m_imp->m_port, alGetFilled(m_imp->m_port)); + + while (!m_imp->m_stopped) + sginap(1); + + if (m_imp->m_oneShotRecording) + st = m_imp->m_st; + else { + st = TSoundTrack::create(m_imp->m_currentFormat, m_imp->m_recordedSampleCount); + TINT32 bytesCopied = 0; + + for (int i = 0; i < (int)m_imp->m_recordedBlocks.size(); ++i) { + memcpy( + (void *)(st->getRawData() + bytesCopied), + m_imp->m_recordedBlocks[i], + m_imp->m_samplePerBlocks[i]); + + delete[] m_imp->m_recordedBlocks[i]; + + bytesCopied += m_imp->m_samplePerBlocks[i]; + } + m_imp->m_samplePerBlocks.clear(); + } + return st; +} + +//------------------------------------------------------------------------------ + +double TSoundInputDevice::getVolume() +{ + ALpv pv[1]; + ALfixed value[2]; + + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + pv[0].param = AL_GAIN; + pv[0].value.ptr = value; + pv[0].sizeIn = 8; + alGetParams(AL_DEFAULT_INPUT, pv, 1); + + double val = (((alFixedToDouble(value[0]) + alFixedToDouble(value[1])) / 2.) + 60.) / 8.05; + return val; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::setVolume(double volume) +{ + ALpv pv[1]; + ALfixed value[2]; + + try { + supportsVolume(); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } + + double val = -60. + 8.05 * volume; + value[0] = alDoubleToFixed(val); + value[1] = alDoubleToFixed(val); + + pv[0].param = AL_GAIN; + pv[0].value.ptr = value; + pv[0].sizeIn = 8; + alSetParams(AL_DEFAULT_INPUT, pv, 1); + + return true; +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::supportsVolume() +{ + ALparamInfo pinfo; + int ret; + ret = alGetParamInfo(AL_DEFAULT_INPUT, AL_GAIN, &pinfo); + double min = alFixedToDouble(pinfo.min.ll); + double max = alFixedToDouble(pinfo.max.ll); + if ((ret != -1) && (min != max) && (max != 0.0)) + return true; + else if ((ret == AL_BAD_PARAM) || ((min == max) && (max == 0.0))) + throw TSoundDeviceException( + TSoundDeviceException::UnableVolume, + "It is impossible to chamge setting of volume"); + else + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, "Output device is not accessible"); +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat( + ULONG sampleRate, int channelCount, int bitPerSample) +{ + TSoundTrackFormat fmt; + int ret; + + if (!m_imp->verifyRate()) + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't any support rate"); + + if (m_imp->m_supportedRate.find((int)sampleRate) == + m_imp->m_supportedRate.end()) { + std::set::iterator it = + m_imp->m_supportedRate.lower_bound((int)sampleRate); + if (it == m_imp->m_supportedRate.end()) { + it = std::max_element(m_imp->m_supportedRate.begin(), + m_imp->m_supportedRate.end()); + if (it != m_imp->m_supportedRate.end()) + sampleRate = *(m_imp->m_supportedRate.rbegin()); + else + throw TSoundDeviceException( + TSoundDeviceException::UnsupportedFormat, + "There isn't a supported rate"); + } else + sampleRate = *it; + } + + int value; + ALvalue vals[32]; + if ((ret = alQueryValues(AL_DEFAULT_INPUT, AL_CHANNELS, vals, 32, 0, 0)) > 0) + for (int i = 0; i < ret; ++i) + value = vals[i].i; + else if (oserror() == AL_BAD_PARAM) + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "It is impossible ask for the max nembers of channels supported"); + else + throw TSoundDeviceException( + TSoundDeviceException::NoMixer, + "It is impossibile information about ouput device"); + + if (value > 2) + value = 2; + if (channelCount > value) + channelCount = value; + else if (channelCount <= 0) + channelCount = 1; + + if (bitPerSample <= 8) + bitPerSample = 8; + else if (bitPerSample <= 16) + bitPerSample = 16; + else + bitPerSample = 24; + + fmt.m_bitPerSample = bitPerSample; + fmt.m_channelCount = channelCount; + fmt.m_sampleRate = sampleRate; + fmt.m_signedSample = true; + + return fmt; +} + +//------------------------------------------------------------------------------ + +TSoundTrackFormat TSoundInputDevice::getPreferredFormat(const TSoundTrackFormat &format) +{ + try { + return getPreferredFormat( + format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); + } catch (TSoundDeviceException &e) { + throw TSoundDeviceException(e.getType(), e.getMessage()); + } +} + +//------------------------------------------------------------------------------ + +bool TSoundInputDevice::isRecording() +{ + return m_imp->m_isRecording; +} + +//****************************************************************************** +//****************************************************************************** +// funzioni per l'interazione con la libreria audio +//****************************************************************************** +//****************************************************************************** +namespace +{ +bool isInterfaceSupported(int deviceType, int interfaceType) +{ + ALvalue vals[16]; + int devNum; + + if ((devNum = alQueryValues(AL_SYSTEM, deviceType, vals, 16, 0, 0)) > 0) { + int i; + for (i = 0; i < devNum; ++i) { + ALpv quals[2]; + quals[0].param = AL_TYPE; + quals[0].value.i = interfaceType; + if (alQueryValues(vals[i].i, AL_INTERFACE, 0, 0, quals, 1) > 0) + return true; + } + } + return false; +} + +//------------------------------------------------------------------------------ + +bool setDefaultInput(TSoundInputDevice::Source type) +{ + string label; + + switch (type) { + case TSoundInputDevice::LineIn: + label = "Line In"; + break; + case TSoundInputDevice::DigitalIn: + label = "AES Input"; + break; + default: + label = "Microphone"; + } + + int dev = alGetResourceByName(AL_SYSTEM, (char *)label.c_str(), AL_DEVICE_TYPE); + if (!dev) + return false; //throw TException("Error to set input device"); + int itf; + if (itf = alGetResourceByName(AL_SYSTEM, (char *)label.c_str(), AL_INTERFACE_TYPE)) { + ALpv p; + + p.param = AL_INTERFACE; + p.value.i = itf; + if (alSetParams(dev, &p, 1) < 0 || p.sizeOut < 0) + return false; //throw TException("Error to set input device"); + } + + ALpv param; + + param.param = AL_DEFAULT_INPUT; + param.value.i = dev; + if (alSetParams(AL_SYSTEM, ¶m, 1) < 0) + return false; //throw TException("Error to set input device"); + + return true; +} + +//------------------------------------------------------------------------------ + +bool setDefaultOutput() +{ + ALpv pvbuf[1]; + + if (!isInterfaceSupported(AL_DEFAULT_OUTPUT, AL_SPEAKER_IF_TYPE)) + return false; //throw TException("Speakers are not supported"); + + int dev = alGetResourceByName(AL_SYSTEM, (char *)"Headphone/Speaker", AL_DEVICE_TYPE); + if (!dev) + return false; //throw TException("invalid device speakers"); + + pvbuf[0].param = AL_DEFAULT_OUTPUT; + pvbuf[0].value.i = dev; + alSetParams(AL_SYSTEM, pvbuf, 1); + + // qui devo metterci gli altoparlanti e poi setto i valori per il default output + pvbuf[0].param = AL_INTERFACE; + pvbuf[0].value.i = AL_SPEAKER_IF_TYPE; + + if (alSetParams(AL_DEFAULT_OUTPUT, pvbuf, 1) < 0) + return false; // throw TException("Unable to set output device params"); + + return true; +} + +//------------------------------------------------------------------------------ + +//return the indexes of all input device of a particular type +list getInputDevice(int deviceType) +{ + ALvalue vals[16]; + ALpv quals[1]; + list devList; + int devNum; + + quals[0].param = AL_TYPE; + quals[0].value.i = deviceType; + if ((devNum = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, vals, 16, quals, 1)) > 0) { + int i; + for (i = 0; i < devNum; ++i) { + int itf; + ALvalue val[16]; + if ((itf = alQueryValues(i, AL_INTERFACE, val, 16, 0, 0)) > 0) { + int j; + for (j = 0; j < itf; ++j) + devList.push_back(vals[j].i); + } + } + } + + return devList; +} + +//------------------------------------------------------------------------------ + +//return the indexes of all input device of a particular type and interface +list getInputDevice(int deviceType, int itfType) +{ + ALvalue vals[16]; + ALpv quals[1]; + list devList; + int devNum; + + quals[0].param = AL_TYPE; + quals[0].value.i = deviceType; + if ((devNum = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, vals, 16, quals, 1)) > 0) { + int i; + for (i = 0; i < devNum; ++i) { + int itf; + ALvalue val[16]; + quals[0].param = AL_TYPE; + quals[0].value.i = itfType; + if ((itf = alQueryValues(i, AL_INTERFACE, val, 16, quals, 1)) > 0) { + int j; + for (j = 0; j < itf; ++j) + devList.push_back(vals[j].i); + } + } + } + + return devList; +} + +//------------------------------------------------------------------------------ + +string getResourceLabel(int resourceID) +{ + ALpv par[1]; + char l[32]; + + par[0].param = AL_LABEL; + par[0].value.ptr = l; + par[0].sizeIn = 32; + + alGetParams(resourceID, par, 1); + + return string(l); +} + +//------------------------------------------------------------------------------ + +// verify the samplerate of the select device is changed from another application +bool isChangeOutput(ULONG sampleRate) +{ + ALpv par[2]; + char l[32]; + + par[0].param = AL_LABEL; + par[0].value.ptr = l; + par[0].sizeIn = 32; + par[1].param = AL_RATE; + + alGetParams(AL_DEFAULT_OUTPUT, par, 2); + if ((strcmp(l, "Analog Out") != 0) || (alFixedToDouble(par[1].value.ll) != sampleRate)) + return true; + else + return false; +} +} diff --git a/toonz/sources/common/tstepparam.h b/toonz/sources/common/tstepparam.h new file mode 100644 index 0000000..14bfcb0 --- /dev/null +++ b/toonz/sources/common/tstepparam.h @@ -0,0 +1,38 @@ + + +#ifndef TSTEPPARAM_H +#define TSTEPPARAM_H + +#include "tdoubleparam.h" +#include "texception.h" +#include "tundo.h" +#include "tparamundo.h" +#include + +#undef DVAPI +#undef DVVAR +#ifdef TNZCORE_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class DVAPI TDoubleStepParam : public TDoubleParam +{ +public: + TDoubleStepParam(double v = 0.0); + TDoubleStepParam(const TDoubleParam &src); + ~TDoubleStepParam(); + + TParam *clone() const { return new TDoubleStepParam(*this); } + + double getValue(double frame, bool cropped = true) const; + + PERSIST_DECLARATION(TDoubleStepParam) +}; + +DEFINE_PARAM_SMARTPOINTER(TDoubleStepParam, double) + +#endif diff --git a/toonz/sources/common/tstream/tpersistset.cpp b/toonz/sources/common/tstream/tpersistset.cpp new file mode 100644 index 0000000..cb68252 --- /dev/null +++ b/toonz/sources/common/tstream/tpersistset.cpp @@ -0,0 +1,71 @@ + + +// TnzCore includes +#include "tstream.h" + +// tcg includes +#include "tcg/tcg_deleter_types.h" +#include "tcg/tcg_function_types.h" + +// STD includes +#include + +#include "tpersistset.h" + +//************************************************************************************** +// TPersistSet implementation +//************************************************************************************** + +PERSIST_IDENTIFIER(TPersistSet, "persistSet") + +//------------------------------------------------------------------ + +TPersistSet::~TPersistSet() +{ + std::for_each(m_objects.begin(), m_objects.end(), + tcg::deleter()); +} + +//------------------------------------------------------------------ + +void TPersistSet::insert(std::auto_ptr object) +{ + struct locals { + inline static bool sameType(TPersist *a, TPersist *b) + { + return (typeid(*a) == typeid(*b)); + } + }; + + // Remove any object with the same type id + std::vector::iterator pt = std::remove_if( + m_objects.begin(), m_objects.end(), + tcg::bind1st(&locals::sameType, object.get())); + + std::for_each(pt, m_objects.end(), tcg::deleter()); + m_objects.erase(pt, m_objects.end()); + + // Push back the supplied object + m_objects.push_back(object.release()); +} + +//------------------------------------------------------------------ + +void TPersistSet::saveData(TOStream &os) +{ + std::vector::iterator pt, pEnd = m_objects.end(); + for (pt = m_objects.begin(); pt != pEnd; ++pt) + os << *pt; +} + +//------------------------------------------------------------------ + +void TPersistSet::loadData(TIStream &is) +{ + while (!is.eos()) { + TPersist *object = 0; + is >> object; + + m_objects.push_back(object); + } +} diff --git a/toonz/sources/common/tstream/tstream.cpp b/toonz/sources/common/tstream/tstream.cpp new file mode 100644 index 0000000..985086b --- /dev/null +++ b/toonz/sources/common/tstream/tstream.cpp @@ -0,0 +1,1417 @@ + + +#include "tstream.h" +#include "tpersist.h" +#include "tfilepath_io.h" +#include "tconvert.h" +#include "tsystem.h" +#include "tutil.h" + +#include "lz4frame_static.h" + +#include + +using namespace std; + +//=============================================================== +namespace +{ + +string escape(string v) +{ + int i = 0; + for (;;) { + i = v.find_first_of("\\\'\"", i); + if (i == (int)string::npos) + break; + string h = "\\" + v[i]; + v.insert(i, "\\"); + i = i + 2; + } + return v; +} + +//=============================================================== + +void writeCompressedFile(TFilePath dst, const string &str) +{ +} + +//=================================================================== + +void readCompressedFile(string &str, TFilePath src) +{ + TFileStatus status(src); + if (!status.doesExist()) + return; + + size_t in_len = status.getSize(); + char *in = (char *)malloc(in_len); + { + Tifstream is(src); + is.read((char *)in, in_len); + } + + LZ4F_decompressionContext_t lz4dctx; + + LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION); + if (LZ4F_isError(err)) + return; + + size_t in_len_read = 0; + + size_t out_len = 1000000, out_len_written, out_len_moved = 0; + void *out = (void *)malloc(out_len); + + while (in_len_read < in_len) { + out_len_written = out_len; + + size_t remaining = LZ4F_decompress(lz4dctx, out, &out_len_written, in, &in_len, NULL); + if (LZ4F_isError(remaining)) + break; + + str.resize(out_len_moved + out_len_written); + + memcpy((void *)(str.c_str() + out_len_moved), (void *)out, out_len_written); + out_len_moved += out_len_written; + } + + LZ4F_freeDecompressionContext(lz4dctx); + + free(in); + free(out); +} + +namespace +{ +// TODO: Unify with tcodec.cpp's version +bool lz4decompress(LZ4F_decompressionContext_t lz4dctx, + char *out, size_t *out_len_res, + const char *in, size_t in_len) +{ + size_t out_len = *out_len_res, + in_read, out_written; + + *out_len_res = 0; + + while (in_len) { + out_written = out_len; + in_read = in_len; + + size_t res = LZ4F_decompress( + lz4dctx, (void *)out, &out_written, (const void *)in, &in_read, NULL); + + if (LZ4F_isError(res)) + return false; + + *out_len_res += out_written; + + out += out_written; + out_len -= out_written; + + in += in_read; + in_len -= in_read; + } + + return true; +} +} // namespace + +//=============================================================== + +class StreamTag +{ +public: + string m_name; + std::map m_attributes; + enum Type { BeginTag, + EndTag, + BeginEndTag }; + Type m_type; + StreamTag() : m_type(BeginTag) {} + + operator bool() const { return m_name != ""; } + + void dump() + { + cout << "name = '" << m_name << "'" << endl; + cout << "type = "; + switch (m_type) { + case BeginTag: + cout << "begin Tag"; + break; + case EndTag: + cout << "end Tag"; + break; + case BeginEndTag: + cout << "begin/end Tag"; + break; + default: + cout << "**bad Tag**"; + break; + } + cout << endl; + std::map::iterator it; + for (it = m_attributes.begin(); it != m_attributes.end(); ++it) { + cout << " '" << it->first << "' = '" << it->second << "'" << endl; + } + } +}; + +//-------------------------------- + +class TPersistFactory +{ + typedef std::map Table; + static TPersistFactory *m_factory; + Table m_table; + TPersistFactory() {} + +public: + static TPersistFactory *instance() + { + if (!m_factory) + m_factory = new TPersistFactory; + return m_factory; + } + void add(string name, TPersistDeclaration *decl) + { + m_table[name] = decl; + } + TPersist *create(string name) + { + Table::iterator it = m_table.find(name); + if (it != m_table.end()) + return (it->second)->create(); + else + return 0; + } +}; + +//-------------------------------- + +TPersistFactory *TPersistFactory::m_factory = 0; + +} // namespace + +//-------------------------------- + +TPersistDeclaration::TPersistDeclaration(const string &id) + : m_id(id) +{ + TPersistFactory::instance()->add(id, this); +} + +//=============================================================== + +TPersist *TPersist::create(const string &name) +{ + return TPersistFactory::instance()->create(name); +} + +//=============================================================== + +class TOStream::Imp +{ +public: + ostream *m_os; + bool m_chanOwner; + bool m_compressed; + ostrstream m_ostrstream; + + vector m_tagStack; + int m_tab; + bool m_justStarted; + typedef map PersistTable; + PersistTable m_table; + int m_maxId; + TFilePath m_filepath; + + Imp() : m_os(0), m_chanOwner(false), m_tab(0), m_justStarted(true), m_maxId(0), m_compressed(false) {} +}; + +//--------------------------------------------------------------- + +TOStream::TOStream(const TFilePath &fp, bool compressed) + : m_imp(new Imp) +{ + m_imp->m_filepath = fp; + + if (compressed) { + m_imp->m_os = &m_imp->m_ostrstream; + m_imp->m_compressed = true; + m_imp->m_chanOwner = false; + } else { + std::auto_ptr os(new Tofstream(fp)); + m_imp->m_os = os->isOpen() ? os.release() : 0; + m_imp->m_chanOwner = true; + } + + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +TOStream::TOStream(Imp *imp) + : m_imp(imp) +{ + assert(!imp->m_tagStack.empty()); + ostream &os = *m_imp->m_os; + if (m_imp->m_justStarted == false) + cr(); + os << "<" << m_imp->m_tagStack.back() << ">"; + m_imp->m_tab++; + cr(); + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +TOStream::~TOStream() +{ + try { + if (!m_imp->m_tagStack.empty()) { + string tagName = m_imp->m_tagStack.back(); + m_imp->m_tagStack.pop_back(); + assert(tagName != ""); + ostream &os = *m_imp->m_os; + m_imp->m_tab--; + if (!m_imp->m_justStarted) + cr(); + os << ""; + cr(); + m_imp->m_justStarted = true; + } else { + if (m_imp->m_compressed) { + const void *in = (const void *)m_imp->m_ostrstream.str(); + size_t in_len = strlen((char *)in); + + size_t out_len = LZ4F_compressFrameBound(in_len, NULL); + void *out = malloc(out_len); + + out_len = LZ4F_compressFrame(out, out_len, in, in_len, NULL); + if (!LZ4F_isError(out_len)) { + Tofstream os(m_imp->m_filepath); + // TNZC + os.write("TABc", 4); + TINT32 v; + v = 0x0A0B0C0D; + os.write((char *)&v, sizeof v); + v = in_len; + os.write((char *)&v, sizeof v); + v = out_len; + os.write((char *)&v, sizeof v); + os.write((char *)out, out_len); + + m_imp->m_ostrstream.freeze(0); + } + + free(out); + } + if (m_imp->m_chanOwner) + delete m_imp->m_os; + delete m_imp; + } + } catch (...) { + } +} + +//--------------------------------------------------------------- + +TFilePath TOStream::getFilePath() +{ + return m_imp->m_filepath; +} + +//--------------------------------------------------------------- + +TFilePath TOStream::getRepositoryPath() +{ + TFilePath fp = m_imp->m_filepath; + return fp.getParentDir() + (fp.getName() + "_files"); +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(int v) +{ + *(m_imp->m_os) << v << " "; + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(double v) +{ + if (areAlmostEqual(v, 0)) //con valori molto piccoli (es. 1.4e-310) non riesce a rileggerli! + v = 0; + + *(m_imp->m_os) << v << " "; + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(string v) +{ + ostream &os = *(m_imp->m_os); + int len = v.length(); + if (len == 0) { + os << "\"\"" + << " "; + m_imp->m_justStarted = false; + return *this; + } + int i; + for (i = 0; i < len; i++) + if (!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') + break; + if (i == len) + os << v << " "; + else { + os << '"' << escape(v) << '"'; + } + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(QString _v) +{ + string v = _v.toStdString(); + + ostream &os = *(m_imp->m_os); + int len = v.length(); + if (len == 0) { + os << "\"\"" + << " "; + m_imp->m_justStarted = false; + return *this; + } + int i; + for (i = 0; i < len; i++) + if (!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') + break; + if (i == len) + os << v << " "; + else { + os << '"' << escape(v) << '"'; + } + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(wstring v) +{ + return operator<<(toString(v)); + /* + ostream &os = *(m_imp->m_os); + int len = v.length(); + if(len==0) + { + os << "\"" << "\"" << " "; + m_imp->m_justStarted = false; + return *this; + } + int i; + for(i=0;im_justStarted = false; + return *this; +*/ +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(const TFilePath &v) +{ + return operator<<(v.getWideString()); +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(const TPixel32 &v) +{ + ostream &os = *(m_imp->m_os); + os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m << " "; + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(const TPixel64 &v) +{ + ostream &os = *(m_imp->m_os); + os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m << " "; + m_imp->m_justStarted = false; + return *this; +} + +//--------------------------------------------------------------- + +void TOStream::cr() +{ + *(m_imp->m_os) << endl; + for (int i = 0; i < m_imp->m_tab; i++) + *(m_imp->m_os) << " "; + m_imp->m_justStarted = false; +} + +//--------------------------------------------------------------- + +TOStream::operator bool() const +{ + return (m_imp->m_os && *m_imp->m_os); +} + +//--------------------------------------------------------------- + +TOStream TOStream::child(string tagName) +{ + assert(tagName != ""); + m_imp->m_tagStack.push_back(tagName); + return TOStream(m_imp); +} + +//--------------------------------------------------------------- + +void TOStream::openChild(string tagName) +{ + assert(tagName != ""); + m_imp->m_tagStack.push_back(tagName); + if (m_imp->m_justStarted == false) + cr(); + *(m_imp->m_os) << "<" << m_imp->m_tagStack.back() << ">"; + m_imp->m_tab++; + cr(); + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +void TOStream::openChild(string tagName, const map &attributes) +{ + assert(tagName != ""); + m_imp->m_tagStack.push_back(tagName); + if (m_imp->m_justStarted == false) + cr(); + *(m_imp->m_os) << "<" << m_imp->m_tagStack.back(); + for (std::map::const_iterator it = attributes.begin(); + it != attributes.end(); ++it) { + *(m_imp->m_os) << " " << it->first + << "=\"" << escape(it->second) << "\""; + } + *(m_imp->m_os) << ">"; + m_imp->m_tab++; + cr(); + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +void TOStream::closeChild() +{ + assert(!m_imp->m_tagStack.empty()); + string tagName = m_imp->m_tagStack.back(); + m_imp->m_tagStack.pop_back(); + assert(tagName != ""); + //ostream &os = *m_imp->m_os; //os non e' usato + m_imp->m_tab--; + if (!m_imp->m_justStarted) + cr(); + *(m_imp->m_os) << ""; + cr(); + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +void TOStream::openCloseChild(string tagName, const map &attributes) +{ + assert(tagName != ""); + // m_imp->m_tagStack.push_back(tagName); + if (m_imp->m_justStarted == false) + cr(); + *(m_imp->m_os) << "<" << tagName; + for (std::map::const_iterator it = attributes.begin(); + it != attributes.end(); ++it) { + *(m_imp->m_os) << " " << it->first + << "=\"" << escape(it->second) << "\""; + } + *(m_imp->m_os) << "/>"; + //m_imp->m_tab++; + cr(); + m_imp->m_justStarted = true; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(TPersist &v) +{ + v.saveData(*this); + return *this; +} + +//--------------------------------------------------------------- + +TOStream &TOStream::operator<<(TPersist *v) +{ + Imp::PersistTable::iterator it = m_imp->m_table.find(v); + if (it != m_imp->m_table.end()) { + *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << it->second << "'/>"; + m_imp->m_justStarted = false; + } else { + int id = ++m_imp->m_maxId; + m_imp->m_table[v] = id; + *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << id << "'>"; + m_imp->m_tab++; + cr(); + v->saveData(*this); + m_imp->m_tab--; + cr(); + *(m_imp->m_os) << "getStreamTag() << ">"; + cr(); + } + return *this; +} + +//--------------------------------------------------------------- + +bool TOStream::checkStatus() const +{ + if (!m_imp->m_os) + return false; + + m_imp->m_os->flush(); + return m_imp->m_os->rdstate() == ios_base::goodbit; +} + +//=============================================================== +/*! + This class contains TIStream's attributes. + It is created by memory allocation in the TIStream's constructor. +*/ +class TIStream::Imp +{ +public: + istream *m_is; + bool m_chanOwner; + int m_line; + string m_strbuffer; + bool m_compressed; + + vector m_tagStack; + + typedef map PersistTable; + PersistTable m_table; + + StreamTag m_currentTag; + + TFilePath m_filepath; + + VersionNumber m_versionNumber; + + Imp() : m_is(0), m_chanOwner(false), m_line(0), m_compressed(false), m_versionNumber(0, 0) {} + + // update m_line if necessary; returns -e if eof + int getNextChar(); + + inline void skipBlanks(); + bool matchTag(); + inline bool match(char c); + bool matchIdent(string &ident); + bool matchValue(string &value); + + void skipCurrentTag(); +}; + +//--------------------------------------------------------------- + +TFilePath TIStream::getFilePath() +{ + return m_imp->m_filepath; +} + +//--------------------------------------------------------------- + +TFilePath TIStream::getRepositoryPath() +{ + TFilePath fp = m_imp->m_filepath; + return fp.getParentDir() + (fp.getName() + "_files"); +} + +//--------------------------------------------------------------- + +int TIStream::Imp::getNextChar() +{ + char c; + m_is->get(c); + if (m_is->eof()) + return -1; + if (c == '\r') + m_line++; + return c; +} + +//--------------------------------------------------------------- + +void TIStream::Imp::skipBlanks() +{ + istream &is = *m_is; + istream::int_type c; + while (c = is.peek(), (isspace(c) || c == '\r')) + getNextChar(); +} + +//--------------------------------------------------------------- + +bool TIStream::Imp::match(char c) +{ + if (m_is->peek() == c) { + getNextChar(); + return true; + } else + return false; +} + +//--------------------------------------------------------------- + +bool TIStream::Imp::matchIdent(string &ident) +{ + istream &is = *m_is; + if (!isalnum(is.peek())) + return false; + ident = ""; + char c; + is.get(c); + ident.append(1, c); + while (c = is.peek(), isalnum(c) || c == '_' || c == '.' || c == '-') { + is.get(c); + ident.append(1, c); + } + return true; +} + +//--------------------------------------------------------------- + +bool TIStream::Imp::matchValue(string &str) +{ + istream &is = *m_is; + char quote = is.peek(); + char c; + if (!is || (quote != '\'' && quote != '\"')) + return false; + is.get(c); + str = ""; + for (;;) { + is.get(c); + if (!is) + throw TException("expected '\"'"); + if (c == quote) + break; + if (c == '\\') { + is.get(c); + if (!is) + throw TException("unexpected EOF"); + if (c != '\'' && c != '\"' && c != '\\') + throw TException("bad escape sequence"); + } + str.append(1, c); + } + if (c != quote) + throw TException("missing '\"'"); + return true; +} + +//--------------------------------------------------------------- + +bool TIStream::Imp::matchTag() +{ + if (m_currentTag) + return true; + StreamTag &tag = m_currentTag; + tag = StreamTag(); + skipBlanks(); + if (!match('<')) + return false; + skipBlanks(); + if (match('!')) { + skipBlanks(); + if (!match('-') || !match('-')) + throw TException("expected '" << (*it)->m_w1 << ")" << endl; + os << "color=(" << color.r << "," << color.g << "," << color.b << ")" << endl; + } + os << endl + << endl + << endl; +} +#else +#define printEdges + +#endif +//----------------------------------------------------------------------------- + +#ifdef _DEBUG +void TVectorImage::Imp::printStrokes(ofstream &os) +{ + for (int i = 0; i < (int)m_strokes.size(); i++) { + os << "*****stroke #" << i << " *****"; + m_strokes[i]->m_s->print(os); + } +} + +#endif + +//----------------------------------------------------------------------------- + +TStroke *TVectorImage::removeEndpoints(int strokeIndex) +{ + return m_imp->removeEndpoints(strokeIndex); +} + +void TVectorImage::restoreEndpoints(int index, TStroke *oldStroke) +{ + m_imp->restoreEndpoints(index, oldStroke); +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::Imp::extendStrokeSmoothly(int index, const TThickPoint &pos, int cpIndex) +{ + TStroke *stroke = m_strokes[index]->m_s; + TGroupId groupId = m_strokes[index]->m_groupId; + + int cpCount = stroke->getControlPointCount(); + int styleId = stroke->getStyle(); + const TThickQuadratic *q = stroke->getChunk(cpIndex == 0 ? 0 : stroke->getChunkCount() - 1); + + double len = q->getLength(); + double w = exp(-len * 0.01); + TThickPoint m = q->getThickP1(); + + TThickPoint p1 = (cpIndex == 0 ? q->getThickP0() : q->getThickP2()) * (1 - w) + m * w; + TThickPoint middleP = (p1 + pos) * 0.5; + + double angle = fabs(cross(normalize(m - middleP), normalize(pos - middleP))); + if (angle < 0.05) + middleP = (m + pos) * 0.5; + + stroke->setControlPoint(cpIndex, middleP); + if (isAlmostZero(len)) { + if (cpIndex == 0) + stroke->setControlPoint(1, middleP * 0.1 + stroke->getControlPoint(2) * 0.9); + else + stroke->setControlPoint(cpCount - 2, middleP * 0.1 + stroke->getControlPoint(cpCount - 3) * 0.9); + } + + vector points(cpCount); + for (int i = 0; i < cpCount - 1; i++) + points[i] = stroke->getControlPoint((cpIndex == 0) ? cpCount - i - 1 : i); + points[cpCount - 1] = pos; + + TStroke *newStroke = new TStroke(points); + newStroke->setStyle(styleId); + newStroke->outlineOptions() = stroke->outlineOptions(); + list oldEdgeList, emptyList; + computeEdgeList(newStroke, m_strokes[index]->m_edgeList, cpIndex == 0, + emptyList, 0, oldEdgeList); + + vector toBeDeleted; + toBeDeleted.push_back(index); + removeStrokes(toBeDeleted, true, false); + + insertStrokeAt(new VIStroke(newStroke, groupId), index, false); + computeRegions(); + transferColors(oldEdgeList, m_strokes[index]->m_edgeList, true, false, true); + + return m_strokes[index]; +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::Imp::extendStroke(int index, const TThickPoint &p, int cpIndex) +{ + TGroupId groupId = m_strokes[index]->m_groupId; + + TStroke *stroke = m_strokes[index]->m_s; + + TStroke *ret; + int cpCount = stroke->getControlPointCount(); + int count = 0; + vector points(cpCount + 2); + int i, incr = (cpIndex == 0) ? -1 : 1; + for (i = ((cpIndex == 0) ? cpCount - 1 : 0); i != cpIndex + incr; i += incr) + points[count++] = stroke->getControlPoint(i); + TThickPoint tp(p, points[count - 1].thick); + points[count++] = 0.5 * (stroke->getControlPoint(cpIndex) + tp); + points[count++] = tp; + + TStroke *newStroke = new TStroke(points); + newStroke->setStyle(stroke->getStyle()); + newStroke->outlineOptions() = stroke->outlineOptions(); + ret = newStroke; + list oldEdgeList, emptyList; + + if (m_computedAlmostOnce) + computeEdgeList(newStroke, m_strokes[index]->m_edgeList, cpIndex == 0, emptyList, false, oldEdgeList); + + vector toBeDeleted; + toBeDeleted.push_back(index); + removeStrokes(toBeDeleted, true, false); + + //removeStroke(index, false); + + insertStrokeAt(new VIStroke(newStroke, groupId), index, false); + + if (m_computedAlmostOnce) { + computeRegions(); + transferColors(oldEdgeList, m_strokes[index]->m_edgeList, true, false, true); + } + return m_strokes[index]; +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::Imp::joinStroke(int index1, int index2, int cpIndex1, int cpIndex2) +{ + assert(m_strokes[index1]->m_groupId == m_strokes[index2]->m_groupId); + + TGroupId groupId = m_strokes[index1]->m_groupId; + + TStroke *stroke1 = m_strokes[index1]->m_s; + TStroke *stroke2 = m_strokes[index2]->m_s; + //TStroke* ret; + int cpCount1 = stroke1->getControlPointCount(); + int cpCount2 = stroke2->getControlPointCount(); + int styleId = stroke1->getStyle(); + + int count = 0; + vector points(cpCount1 + ((index1 != index2) ? cpCount2 : 1) + 1); + int i, incr = (cpIndex1 == 0) ? -1 : 1; + for (i = ((cpIndex1 == 0) ? cpCount1 - 1 : 0); i != cpIndex1 + incr; i += incr) + points[count++] = stroke1->getControlPoint(i); + points[count++] = 0.5 * (stroke1->getControlPoint(cpIndex1) + stroke2->getControlPoint(cpIndex2)); + if (index1 != index2) { + incr = (cpIndex2 == 0) ? 1 : -1; + for (i = cpIndex2; i != ((cpIndex2 == 0) ? cpCount2 - 1 : 0) + incr; i += incr) + points[count++] = stroke2->getControlPoint(i); + } else + points[count++] = stroke2->getControlPoint(cpIndex2); + + TStroke *newStroke = new TStroke(points); + newStroke->setStyle(styleId); + newStroke->outlineOptions() = stroke1->outlineOptions(); + //ret = newStroke; + if (index1 == index2) + newStroke->setSelfLoop(); + list oldEdgeList, emptyList; + + computeEdgeList(newStroke, m_strokes[index1]->m_edgeList, cpIndex1 == 0, + (index1 != index2) ? m_strokes[index2]->m_edgeList : emptyList, + cpIndex2 == 0, oldEdgeList); + + vector toBeDeleted; + toBeDeleted.push_back(index1); + if (index1 != index2) + toBeDeleted.push_back(index2); + removeStrokes(toBeDeleted, true, false); + + insertStrokeAt(new VIStroke(newStroke, groupId), index1, false); + computeRegions(); + transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); + return m_strokes[index1]; +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::Imp::joinStrokeSmoothly(int index1, int index2, int cpIndex1, int cpIndex2) +{ + assert(m_strokes[index1]->m_groupId == m_strokes[index2]->m_groupId); + + TGroupId groupId = m_strokes[index1]->m_groupId; + + TStroke *stroke1 = m_strokes[index1]->m_s; + TStroke *stroke2 = m_strokes[index2]->m_s; + TStroke *ret; + int cpCount1 = stroke1->getControlPointCount(); + int cpCount2 = stroke2->getControlPointCount(); + int styleId = stroke1->getStyle(); + + int qCount1 = stroke1->getChunkCount(); + int qCount2 = stroke2->getChunkCount(); + const TThickQuadratic *q1 = stroke1->getChunk(cpIndex1 == 0 ? 0 : qCount1 - 1); + const TThickQuadratic *q2 = stroke2->getChunk(cpIndex2 == 0 ? 0 : qCount2 - 1); + + double len1 = q1->getLength(); + assert(len1 >= 0); + if (len1 <= 0) + len1 = 0; + double w1 = exp(-len1 * 0.01); + + double len2 = q2->getLength(); + assert(len2 >= 0); + if (len2 <= 0) + len2 = 0; + double w2 = exp(-len2 * 0.01); + + TThickPoint extreme1 = cpIndex1 == 0 ? q1->getThickP0() : q1->getThickP2(); + TThickPoint extreme2 = cpIndex2 == 0 ? q2->getThickP0() : q2->getThickP2(); + + TThickPoint m1 = q1->getThickP1(); + TThickPoint m2 = q2->getThickP1(); + + TThickPoint p1 = extreme1 * (1 - w1) + m1 * w1; + TThickPoint p2 = extreme2 * (1 - w2) + m2 * w2; + + TThickPoint middleP = (p1 + p2) * 0.5; + + double angle = fabs(cross(normalize(m1 - middleP), normalize(m2 - middleP))); + if (angle < 0.05) + middleP = (m1 + m2) * 0.5; + + stroke1->setControlPoint(cpIndex1, middleP); + if (isAlmostZero(len1)) { + if (cpIndex1 == 0) + stroke1->setControlPoint(1, middleP * 0.1 + stroke1->getControlPoint(2) * 0.9); + else + stroke1->setControlPoint(cpCount1 - 2, middleP * 0.1 + stroke1->getControlPoint(cpCount1 - 3) * 0.9); + } + + stroke2->setControlPoint(cpIndex2, middleP); + if (isAlmostZero(len2)) { + if (cpIndex2 == 0) + stroke2->setControlPoint(1, middleP * 0.1 + stroke2->getControlPoint(2) * 0.9); + else + stroke2->setControlPoint(cpCount2 - 2, middleP * 0.1 + stroke2->getControlPoint(cpCount2 - 3) * 0.9); + } + + if (stroke1 == stroke2) { + list oldEdgeList, emptyList; + computeEdgeList(stroke1, m_strokes[index1]->m_edgeList, cpIndex1 == 0, + emptyList, false, oldEdgeList); + eraseIntersection(index1); + m_strokes[index1]->m_isNewForFill = true; + stroke1->setSelfLoop(); + computeRegions(); + transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); + return m_strokes[index1]; + //nundo->m_newStroke=new TStroke(*stroke1); + //nundo->m_newStrokeId=stroke1->getId(); + } + + vector points; + points.reserve(cpCount1 + cpCount2 - 1); + + int incr = (cpIndex1) ? 1 : -1; + int stop = cpIndex1; + + int i = cpCount1 - 1 - cpIndex1; + for (; i != stop; i += incr) + points.push_back(stroke1->getControlPoint(i)); + + incr = (cpIndex2) ? -1 : 1; + stop = cpCount2 - 1 - cpIndex2; + + for (i = cpIndex2; i != stop; i += incr) + points.push_back(stroke2->getControlPoint(i)); + + points.push_back(stroke2->getControlPoint(stop)); + + TStroke *newStroke = new TStroke(points); + newStroke->setStyle(styleId); + newStroke->outlineOptions() = stroke1->outlineOptions(); + ret = newStroke; + //nundo->m_newStroke=new TStroke(*newStroke); + //nundo->m_newStrokeId=newStroke->getId(); + list oldEdgeList; + //ofstream os("c:\\temp\\edges.txt"); + + //printEdges(os, "****edgelist1", getPalette(), m_imp->m_strokes[index1]->m_edgeList); + //printEdges(os, "****edgelist2", getPalette(), m_imp->m_strokes[index2]->m_edgeList); + + computeEdgeList(newStroke, m_strokes[index1]->m_edgeList, cpIndex1 == 0, + m_strokes[index2]->m_edgeList, cpIndex2 == 0, oldEdgeList); + //printEdges(os, "****edgelist", getPalette(), oldEdgeList); + + vector toBeDeleted; + toBeDeleted.push_back(index1); + toBeDeleted.push_back(index2); + removeStrokes(toBeDeleted, true, false); + + insertStrokeAt(new VIStroke(newStroke, groupId), index1); + computeRegions(); + transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); + + return m_strokes[index1]; + + // TUndoManager::manager()->add(nundo); +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::joinStroke(int index1, int index2, int cpIndex1, int cpIndex2, bool isSmooth) +{ + int finalStyle = -1; + + if (index1 > index2) { + finalStyle = getStroke(index1)->getStyle(); + tswap(index1, index2); + tswap(cpIndex1, cpIndex2); + } + /* + if (index1==index2) //selfLoop! + { + if (index1>0 && index1<(int)getStrokeCount()-1 && + !getStroke(index1-1)->isSelfLoop() && + !getStroke(index1+1)->isSelfLoop()) + { + for (UINT i = index1+2; iisSelfLoop(); i++) + ; + moveStroke(index1, i-1); + index1 = index2 = i-1; + } + } + */ + VIStroke *ret; + if (isSmooth) + ret = m_imp->joinStrokeSmoothly(index1, index2, cpIndex1, cpIndex2); + else + ret = m_imp->joinStroke(index1, index2, cpIndex1, cpIndex2); + + if (finalStyle != -1) + getStroke(index1)->setStyle(finalStyle); + return ret; +} + +//----------------------------------------------------------------------------- + +VIStroke *TVectorImage::extendStroke(int index, const TThickPoint &p, int cpIndex, bool isSmooth) +{ + + if (isSmooth) + return m_imp->extendStrokeSmoothly(index, p, cpIndex); + else + return m_imp->extendStroke(index, p, cpIndex); +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +TInputStreamInterface &TInputStreamInterface::operator>>(TPixel32 &pixel) +{ + return *this >> pixel.r >> pixel.g >> pixel.b >> pixel.m; +} + +//------------------------------------------------------------------- + +TOutputStreamInterface &TOutputStreamInterface::operator<<(const TPixel32 &pixel) +{ + return *this << pixel.r << pixel.g << pixel.b << pixel.m; +} + +//------------------------------------------------------------------- + +void TVectorImage::setAutocloseTolerance(double val) +{ + m_imp->m_autocloseTolerance = val; +} + +//------------------------------------------------------------------- + +double TVectorImage::getAutocloseTolerance() const +{ + return m_imp->m_autocloseTolerance; +} + +//------------------------------------------------------------------- + +TThread::Mutex *TVectorImage::getMutex() const +{ + return m_imp->m_mutex; +} + +//------------------------------------------------------------------- + +void TVectorImage::areaFill(TStroke *stroke, int index, bool m_onlyUnfilled) +{ + TVectorImage v; + v.addStroke(stroke); + v.findRegions(); + + for (UINT i = 0; i < v.getRegionCount(); i++) + for (UINT j = 0; j < getRegionCount(); j++) { + if (m_imp->m_insideGroup != TGroupId() && !m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[getRegion(j)->getEdge(0)->m_index]->m_groupId)) + continue; + + if (v.getRegion(i)->contains(*getRegion(j))) + getRegion(j)->setStyle(index); + } + + v.removeStroke(0); +} + +VIStroke *cloneVIStroke(VIStroke *vs) +{ + return new VIStroke(*vs); +} + +void deleteVIStroke(VIStroke *vs) +{ + delete vs; + vs = 0; +} + +//------------------------------------------------------------------- + +bool TVectorImage::sameSubGroup(int index0, int index1) const +{ + if (index0 < 0 || index1 < 0) + return 0; + return m_imp->m_strokes[index0]->m_groupId.getCommonParentDepth(m_imp->m_strokes[index1]->m_groupId) > m_imp->m_insideGroup.getDepth(); +} + +//------------------------------------------------------------------- + +int TVectorImage::getCommonGroupDepth(int index0, int index1) const +{ + if (index0 < 0 || index1 < 0) + return 0; + return m_imp->m_strokes[index0]->m_groupId.getCommonParentDepth(m_imp->m_strokes[index1]->m_groupId); +} + +//------------------------------------------------------------------- + +int TVectorImage::ungroup(int fromIndex) +{ + m_imp->m_insideGroup = TGroupId(); + + assert(m_imp->m_strokes[fromIndex]->m_groupId.isGrouped() != 0); + vector changedStrokes; + + int toIndex = fromIndex + 1; + + while (toIndex < (int)m_imp->m_strokes.size() && m_imp->m_strokes[fromIndex]->m_groupId.getCommonParentDepth(m_imp->m_strokes[toIndex]->m_groupId) >= 1) + toIndex++; + + toIndex--; + + TGroupId groupId; + + if (fromIndex > 0 && m_imp->m_strokes[fromIndex - 1]->m_groupId.isGrouped(true) != 0) + groupId = m_imp->m_strokes[fromIndex - 1]->m_groupId; + else if (toIndex < (int)m_imp->m_strokes.size() - 1 && m_imp->m_strokes[toIndex + 1]->m_groupId.isGrouped(true) != 0) + groupId = m_imp->m_strokes[toIndex + 1]->m_groupId; + else + groupId = TGroupId(this, true); + + for (int i = fromIndex; i <= toIndex || (i < (int)m_imp->m_strokes.size() && m_imp->m_strokes[i]->m_groupId.isGrouped(true) != 0); i++) { + m_imp->m_strokes[i]->m_groupId.ungroup(groupId); + changedStrokes.push_back(i); + } + + notifyChangedStrokes(changedStrokes, vector(), false); + + return toIndex - fromIndex + 1; +} + +//------------------------------------------------------------------- + +bool TVectorImage::isEnteredGroupStroke(int index) const +{ + return m_imp->m_insideGroup.isParentOf(getVIStroke(index)->m_groupId); +} + +//------------------------------------------------------------------- + +bool TVectorImage::enterGroup(int index) +{ + VIStroke *vs = getVIStroke(index); + + if (!vs->m_groupId.isGrouped()) + return false; + + int newDepth = vs->m_groupId.getCommonParentDepth(m_imp->m_insideGroup) + 1; + + TGroupId newGroupId = vs->m_groupId; + + while (newGroupId.getDepth() > newDepth) + newGroupId = newGroupId.getParent(); + + if (newGroupId == m_imp->m_insideGroup) + return false; + + m_imp->m_insideGroup = newGroupId; + return true; +} + +//------------------------------------------------------------------- + +int TVectorImage::exitGroup() +{ + if (m_imp->m_insideGroup == TGroupId()) + return -1; + + int i, ret = -1; + for (i = 0; i < (int)m_imp->m_strokes.size(); i++) { + if (m_imp->m_strokes[i]->m_groupId.getCommonParentDepth(m_imp->m_insideGroup) >= m_imp->m_insideGroup.getDepth()) { + ret = i; + break; + } + } + + assert(i != m_imp->m_strokes.size()); + + m_imp->m_insideGroup = m_imp->m_insideGroup.getParent(); + return ret; +} + +//------------------------------------------------------------------- + +void TVectorImage::group(int fromIndex, int count) +{ + int i; + assert(count >= 0); + vector changedStroke; + + TGroupId parent = TGroupId(this, false); + + for (i = 0; i < count; i++) { + m_imp->m_strokes[fromIndex + i]->m_groupId = TGroupId(parent, m_imp->m_strokes[fromIndex + i]->m_groupId); + changedStroke.push_back(fromIndex + i); + } + + m_imp->rearrangeMultiGroup(); //see method's comment + + m_imp->regroupGhosts(changedStroke); + + notifyChangedStrokes(changedStroke, vector(), false); + +#ifdef _DEBUG + m_imp->checkGroups(); +#endif +} + +//------------------------------------------------------------------- + +int TVectorImage::getGroupDepth(UINT index) const +{ + assert(index < m_imp->m_strokes.size()); + + return m_imp->m_strokes[index]->m_groupId.isGrouped(); +} + +//------------------------------------------------------------------- + +int TVectorImage::areDifferentGroup(UINT index1, bool isRegion1, UINT index2, bool isRegion2) const +{ + return m_imp->areDifferentGroup(index1, isRegion1, index2, isRegion2); +} + +//------------------------------------------------------------------- +/*this method is tricky. +it is not allow to have not-adiacent strokes of same group. +but it can happen when you group some already-grouped strokes creating sub-groups. + +example: vi made of 5 strokes, before grouping (N=no group) +N +N +1 +1 +N +after grouping became: +2 +2 +2-1 +2-1 +2 +not allowed! + +this method moves strokes, so that adiacent strokes have same group. +so after calling rearrangeMultiGroup the vi became: +2 +2 +2 +2-1 +2-1 + +*/ + +void TVectorImage::Imp::rearrangeMultiGroup() +{ + UINT i, j, k; + if (m_strokes.size() <= 0) + return; + for (i = 0; i < m_strokes.size() - 1; i++) { + if (m_strokes[i]->m_groupId.isGrouped() && m_strokes[i + 1]->m_groupId.isGrouped() && m_strokes[i]->m_groupId != m_strokes[i + 1]->m_groupId) { + TGroupId &prevId = m_strokes[i]->m_groupId; + TGroupId &idToMove = m_strokes[i + 1]->m_groupId; + for (j = i + 1; j < m_strokes.size() && m_strokes[j]->m_groupId == idToMove; j++) + ; + if (j != m_strokes.size()) { + j--; //now range i+1-j contains the strokes to be moved. + //let's compute where to move them (after last + for (k = j; k < m_strokes.size() && m_strokes[k]->m_groupId != prevId; k++) + ; + if (k < m_strokes.size()) { + for (; k < m_strokes.size() && m_strokes[k]->m_groupId == prevId; k++) + ; + moveStrokes(i + 1, j - i, k, false); + rearrangeMultiGroup(); + return; + } + } + } + } +} + +//------------------------------------------------------------------- + +int TVectorImage::Imp::areDifferentGroup(UINT index1, bool isRegion1, UINT index2, bool isRegion2) const +{ + TGroupId group1, group2; + + if (isRegion1) { + TRegion *r = m_regions[index1]; + for (UINT i = 0; i < r->getEdgeCount(); i++) + if (r->getEdge(i)->m_index >= 0) { + group1 = m_strokes[r->getEdge(i)->m_index]->m_groupId; + break; + } + } else + group1 = m_strokes[index1]->m_groupId; + if (isRegion2) { + TRegion *r = m_regions[index2]; + for (UINT i = 0; i < r->getEdgeCount(); i++) + if (r->getEdge(i)->m_index >= 0) { + group2 = m_strokes[r->getEdge(i)->m_index]->m_groupId; + break; + } + } else + group2 = m_strokes[index2]->m_groupId; + + if (!group1 && !group2) + return 0; + + if (group1 == group2) + return -1; + else + return group1.getCommonParentDepth(group2); +} + +//------------------------------------------------------------------- + +int TGroupId::getCommonParentDepth(const TGroupId &id) const +{ + int size1 = m_id.size(); + int size2 = id.m_id.size(); + int count; + + for (count = 0; count < tmin(size1, size2); count++) + if (m_id[size1 - count - 1] != id.m_id[size2 - count - 1]) + break; + + return count; +} + +//------------------------------------------------------------------- + +TGroupId::TGroupId(const TGroupId &parent, const TGroupId &id) +{ + assert(parent.m_id[0] > 0); + assert(id.m_id.size() > 0); + + if (id.isGrouped(true) != 0) + m_id.push_back(parent.m_id[0]); + else { + m_id = id.m_id; + int i; + for (i = 0; i < (int)parent.m_id.size(); i++) + m_id.push_back(parent.m_id[i]); + } +} + +/* +bool TGroupId::sameParent(const TGroupId& id) const +{ +assert(!m_id.empty() || !id.m_id.empty()); +return m_id.back()==id.m_id.back(); +} +*/ + +TGroupId TGroupId::getParent() const +{ + if (m_id.size() <= 1) + return TGroupId(); + + TGroupId ret = *this; + ret.m_id.erase(ret.m_id.begin()); + return ret; +} + +void TGroupId::ungroup(const TGroupId &id) +{ + assert(id.isGrouped(true) != 0); + assert(!m_id.empty()); + + if (m_id.size() == 1) + m_id[0] = id.m_id[0]; + else + m_id.pop_back(); +} + +bool TGroupId::operator==(const TGroupId &id) const +{ + if (m_id.size() != id.m_id.size()) + return false; + UINT i; + for (i = 0; i < m_id.size(); i++) + if (m_id[i] != id.m_id[i]) + return false; + + return true; +} + +bool TGroupId::operator<(const TGroupId &id) const +{ + assert(!m_id.empty() && !id.m_id.empty()); + int size1 = m_id.size(); + int size2 = id.m_id.size(); + int i; + for (i = 0; i < tmin(size1, size2); i++) + if (m_id[size1 - i - 1] != id.m_id[size2 - i - 1]) + return m_id[size1 - i - 1] < id.m_id[size2 - i - 1]; + + return size1 < size2; +} + +//------------------------------------------------------------------- + +int TGroupId::isGrouped(bool implicit) const +{ + assert(!m_id.empty()); + assert(m_id[0] != 0); + if (implicit) + return (m_id[0] < 0) ? 1 : 0; + else + return (m_id[0] > 0) ? m_id.size() : 0; +} + +//------------------------------------------------------------------- + +TGroupId::TGroupId(TVectorImage *vi, bool isGhost) +{ + m_id.push_back((isGhost) ? -(++vi->m_imp->m_maxGhostGroupId) : ++vi->m_imp->m_maxGroupId); +} + +#ifdef _DEBUG +void TVectorImage::Imp::checkGroups() +{ + TGroupId currGroupId; + set groupSet; + set::iterator it; + UINT i = 0; + + while (i < m_strokes.size()) { + //assert(m_strokes[i]->m_groupId!=currGroupId); + //assert(i==0 || m_strokes[i-1]->m_groupId.isGrouped()!=m_strokes[i]->m_groupId.isGrouped()!=0 || + // (m_strokes[i]->m_groupId.isGrouped()!=0 && m_strokes[i-1]->m_groupId!=m_strokes[i]->m_groupId)); + + currGroupId = m_strokes[i]->m_groupId; + it = groupSet.find(currGroupId); + if (it != groupSet.end()) //esisteva gia un gruppo con questo id! + assert(!"errore: due gruppi con lo stesso id!"); + else + groupSet.insert(currGroupId); + + while (i < m_strokes.size() && m_strokes[i]->m_groupId == currGroupId) + i++; + } +} +#endif + +//------------------------------------------------------------------- + +bool TVectorImage::canMoveStrokes(int strokeIndex, int count, int moveBefore) const +{ + return m_imp->canMoveStrokes(strokeIndex, count, moveBefore); +} + +//------------------------------------------------------------------- + +//verifica se si possono spostare le stroke da strokeindex a strokeindex+count-1 prima della posizione moveBefore; +//per fare questo fa un vettore in cui mette tutti i gruppi nella posizione dopo lo +//spostamento e verifica che sia un configurazione di gruppi ammessa. + +bool TVectorImage::Imp::canMoveStrokes(int strokeIndex, int count, int moveBefore) const +{ + if (m_maxGroupId <= 1) //non ci sono gruppi! + return true; + + int i, j = 0; + + vector groupsAfterMoving(m_strokes.size()); + if (strokeIndex < moveBefore) { + for (i = 0; i < strokeIndex; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = strokeIndex + count; i < moveBefore; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = strokeIndex; i < strokeIndex + count; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = moveBefore; i < (int)m_strokes.size(); i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + } else { + for (i = 0; i < moveBefore; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = strokeIndex; i < strokeIndex + count; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = moveBefore; i < strokeIndex; i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + + for (i = strokeIndex + count; i < (int)m_strokes.size(); i++) + groupsAfterMoving[j++] = m_strokes[i]->m_groupId; + } + + assert(j == (int)m_strokes.size()); + + i = 0; + TGroupId currGroupId; + set groupSet; + + while (i < (int)groupsAfterMoving.size()) { + currGroupId = groupsAfterMoving[i]; + if (groupSet.find(currGroupId) != groupSet.end()) //esisteva gia un gruppo con questo id! + { + if (!currGroupId.isGrouped(true)) //i gruppi impliciti non contano + return false; + } else + groupSet.insert(currGroupId); + + while (i < (int)groupsAfterMoving.size() && groupsAfterMoving[i] == currGroupId) + i++; + } + + return true; +} + +//----------------------------------------------------------------- + +void TVectorImage::Imp::regroupGhosts(vector &changedStrokes) +{ + TGroupId currGroupId; + set groupMap; + set::iterator it; + UINT i = 0; + + while (i < m_strokes.size()) { + assert(m_strokes[i]->m_groupId != currGroupId); + assert(i == 0 || m_strokes[i - 1]->m_groupId.isGrouped() != m_strokes[i]->m_groupId.isGrouped() != 0 || + (m_strokes[i]->m_groupId.isGrouped() != 0 && m_strokes[i - 1]->m_groupId != m_strokes[i]->m_groupId)); + + currGroupId = m_strokes[i]->m_groupId; + it = groupMap.find(currGroupId); + if (it != groupMap.end()) //esisteva gia un gruppo con questo id! + { + if (currGroupId.isGrouped() != 0) + assert(!"errore: due gruppi con lo stesso id!"); + else //gruppo ghost; gli do un nuovo id + { + TGroupId newGroup(m_vi, true); + + while (i < m_strokes.size() && m_strokes[i]->m_groupId.isGrouped(true) != 0) { + m_strokes[i]->m_groupId = newGroup; + changedStrokes.push_back(i); + i++; + } + } + } else { + groupMap.insert(currGroupId); + while (i < m_strokes.size() && + ((currGroupId.isGrouped(false) != 0 && m_strokes[i]->m_groupId == currGroupId) || + currGroupId.isGrouped(true) != 0 && m_strokes[i]->m_groupId.isGrouped(true) != 0)) { + if (m_strokes[i]->m_groupId != currGroupId) { + m_strokes[i]->m_groupId = currGroupId; + changedStrokes.push_back(i); + } + i++; + } + } + } +} + +//-------------------------------------------------------------- + +bool TVectorImage::canEnterGroup(int strokeIndex) const +{ + VIStroke *vs = m_imp->m_strokes[strokeIndex]; + + if (!vs->m_groupId.isGrouped()) + return false; + + return m_imp->m_insideGroup == TGroupId() || + vs->m_groupId != m_imp->m_insideGroup; +} + +//-------------------------------------------------------------- + +bool TVectorImage::inCurrentGroup(int strokeIndex) const +{ + return m_imp->inCurrentGroup(strokeIndex); +} + +//---------------------------------------------------------------------------------- + +bool TVectorImage::Imp::inCurrentGroup(int strokeIndex) const +{ + return m_insideGroup == TGroupId() || m_insideGroup.isParentOf(m_strokes[strokeIndex]->m_groupId); +} + +//-------------------------------------------------------------------------------------------------- + +bool TVectorImage::selectable(int strokeIndex) const +{ + return (m_imp->m_insideGroup != m_imp->m_strokes[strokeIndex]->m_groupId && + inCurrentGroup(strokeIndex)); +} + +//-------------------------------------------------------------------------------------------------- +namespace +{ + +bool containsNoSubregion(const TRegion *r, const TPointD &p) +{ + + if (r->contains(p)) { + for (unsigned int i = 0; i < r->getSubregionCount(); i++) + if (r->getSubregion(i)->contains(p)) + return false; + return true; + } else + return false; +} +}; + +//------------------------------------------------------ + +int TVectorImage::getGroupByStroke(UINT index) const +{ + VIStroke *viStroke = getVIStroke(index); + return viStroke->m_groupId.m_id.back(); +} + +//------------------------------------------------------ + +int TVectorImage::getGroupByRegion(UINT index) const +{ + TRegion *r = m_imp->m_regions[index]; + for (UINT i = 0; i < r->getEdgeCount(); i++) + if (r->getEdge(i)->m_index >= 0) { + return m_imp->m_strokes[r->getEdge(i)->m_index]->m_groupId.m_id.back(); + } + + return -1; +} + +//------------------------------------------------------ + +int TVectorImage::pickGroup(const TPointD &pos, bool onEnteredGroup) const +{ + if (onEnteredGroup && isInsideGroup() == 0) + return -1; + + //double maxDist2 = 50*tglGetPixelSize2(); + + int strokeIndex = getStrokeCount() - 1; + + while (strokeIndex >= 0) // ogni ciclo di while esplora un gruppo; ciclo sugli stroke + { + if (!isStrokeGrouped(strokeIndex)) { + strokeIndex--; + continue; + } + + bool entered = isInsideGroup() > 0 && isEnteredGroupStroke(strokeIndex); + + if ((onEnteredGroup || entered) && + (!onEnteredGroup || !entered)) { + strokeIndex--; + continue; + } + + int currStrokeIndex = strokeIndex; + + while (strokeIndex >= 0 && getCommonGroupDepth(strokeIndex, currStrokeIndex) > 0) { + TStroke *s = getStroke(strokeIndex); + double outT; + int chunkIndex; + double dist2; + bool ret = s->getNearestChunk(pos, outT, chunkIndex, dist2); + if (ret) { + TThickPoint p = s->getChunk(chunkIndex)->getThickPoint(outT); + if (p.thick < 0.1) + p.thick = 1; + if (sqrt(dist2) <= 1.5 * p.thick) + return strokeIndex; + } + + /*TThickPoint p = s->getThickPoint(s->getW(pos)); + + double dist = tdistance( TThickPoint(pos,0), p); + if (dist<1.2*p.thick/2.0) + return strokeIndex;*/ + strokeIndex--; + } + } + + strokeIndex = getStrokeCount() - 1; + int ret = -1; + + while (strokeIndex >= 0) // ogni ciclo di while esplora un gruppo; ciclo sulle regions + { + if (!isStrokeGrouped(strokeIndex)) { + strokeIndex--; + continue; + } + + bool entered = isInsideGroup() > 0 && isEnteredGroupStroke(strokeIndex); + + if ((onEnteredGroup || entered) && + (!onEnteredGroup || !entered)) { + strokeIndex--; + continue; + } + + TRegion *currR = 0; + for (UINT regionIndex = 0; regionIndex < getRegionCount(); regionIndex++) { + TRegion *r = getRegion(regionIndex); + + int i, regionStrokeIndex = -1; + for (i = 0; i < (int)r->getEdgeCount() && regionStrokeIndex < 0; i++) + regionStrokeIndex = r->getEdge(i)->m_index; + + if (regionStrokeIndex >= 0 && sameSubGroup(regionStrokeIndex, strokeIndex) && containsNoSubregion(r, pos)) { + if (!currR || currR->contains(*r)) { + currR = r; + ret = regionStrokeIndex; + } + } + } + if (currR != 0) { + assert(m_palette); + const TSolidColorStyle *st = dynamic_cast(m_palette->getStyle(currR->getStyle())); + if (!st || st->getMainColor() != TPixel::Transparent) + return ret; + } + + while (strokeIndex > 0 && getCommonGroupDepth(strokeIndex, strokeIndex - 1) > 0) + strokeIndex--; + strokeIndex--; + } + + return -1; +} + +//------------------------------------------------------------------------------------ + +int TVectorImage::pickGroup(const TPointD &pos) const +{ + int index; + if ((index = pickGroup(pos, true)) == -1) + return pickGroup(pos, false); + + return index; +} + +//-------------------------------------------------------------------------------------------------- diff --git a/toonz/sources/common/tvectorimage/tvectorimageP.h b/toonz/sources/common/tvectorimage/tvectorimageP.h new file mode 100644 index 0000000..e81e7b8 --- /dev/null +++ b/toonz/sources/common/tvectorimage/tvectorimageP.h @@ -0,0 +1,252 @@ + + +#ifndef _TVECTORIMAGEP_H_ +#define _TVECTORIMAGEP_H_ + +#include "tstroke.h" +#include "tvectorimage.h" +#include "tregion.h" +#include "tcurves.h" +using namespace std; + +//----------------------------------------------------------------------------- + +class IntersectedStroke; +class VIStroke; + +//============================================================================= + +class VIStroke; + +class TGroupId +{ + +public: + vector m_id; //m_id[i-1] e' parent di m_id[i] + TGroupId() + : m_id() {} + + //ghost group sono i gruppi impliciti: tutti gli stroke che non fanno parte di nessun gruppo ma + //che stanno tra due gruppi fanno parte di un gruppo implicito. per convenzione un ghostGroup ha id<0 + + TGroupId(TVectorImage *vi, bool isGhost); + + TGroupId(const TGroupId &strokeGroup) : m_id(strokeGroup.m_id){}; + + //costruisce un gruppo partendo da un parent e da un id esistente. + TGroupId(const TGroupId &parent, const TGroupId &id); + + bool operator==(const TGroupId &id) const; + bool operator!=(const TGroupId &id) const { return !(*this == id); } + + //TGroupId makeGroup(vector strokes, bool recomputeRegions=false); + //void unmakeGroup(vector strokes); + + //ritrona la depth del gruppo. (0->not grouped) + int isGrouped(bool implicit = false) const; + + //toglie il parent; se nera gruppo semplice, gli assegna il parametro id. + void ungroup(const TGroupId &id); + //bool sameParent(const TGroupId& id) const; + + bool operator!() const { return m_id.empty() || m_id[0] == 0; }; + bool operator<(const TGroupId &id) const; + + int getDepth() const { return m_id.size(); } + int getCommonParentDepth(const TGroupId &id) const; + TGroupId getParent() const; + + int isParentOf(const TGroupId &id) const { return getCommonParentDepth(id) == getDepth(); } +}; + +class VIStroke +{ +public: + TStroke *m_s; + bool m_isPoint; + bool m_isNewForFill; + list m_edgeList; + TGroupId m_groupId; + + VIStroke(TStroke *s, const TGroupId &StrokeId) + : m_s(s), m_isPoint(false), m_isNewForFill(true), m_groupId(StrokeId){}; + + VIStroke(const VIStroke &s, bool sameId = true); + + ~VIStroke() + { + delete m_s; + + list::iterator it, it_b = m_edgeList.begin(), it_e = m_edgeList.end(); + for (it = it_b; it != it_e; ++it) + if ((*it)->m_toBeDeleted) + delete *it; + } + + void inline addEdge(TEdge *e) { m_edgeList.push_back(e); } + + bool inline removeEdge(TEdge *e) + { + list::iterator it = m_edgeList.begin(); + while (it != m_edgeList.end() && *it != e) + it++; + if (*it == e) { + m_edgeList.erase(it); + return true; + } + return false; + } +}; + +//----------------------------------------------------------------------------- +class IntersectionData; +class Intersection; +//class IntersectStroke; + +#ifdef LEVO + +class TAutocloseEdge : public TGeneralEdge +{ +public: + TSegment m_segment; + int m_nextStrokeIndex; + double m_nextStrokeW; + + TAutocloseEdge(const TSegment &segment, int nextStrokeIndex, double nextStrokeW) + : TGeneralEdge(eAutoclose), m_segment(segment), m_nextStrokeIndex(nextStrokeIndex), m_nextStrokeW(nextStrokeW) + { + } +}; + +#endif + +//--------------------------------------------------------------------------------------------------- +class TRegionFinder; + +class TVectorImage::Imp +{ + TVectorImage *m_vi; + +public: + int m_maxGroupId; + int m_maxGhostGroupId; + + bool m_areValidRegions; + bool m_computedAlmostOnce; + bool m_justLoaded; + bool m_minimizeEdges; + bool m_notIntersectingStrokes, m_computeRegions; + TGroupId m_insideGroup; + + vector m_strokes; + double m_autocloseTolerance; + IntersectionData *m_intersectionData; + vector m_regions; + TThread::Mutex *m_mutex; + Imp(TVectorImage *vi); + ~Imp(); + + void initRegionsData(); + void deleteRegionsData(); + + TRegion *getRegion(const TPointD &p); + + int fill(const TPointD &p, int styleId); + bool selectFill(const TRectD &selectArea, TStroke *s, int styleId, bool onlyUnfilled, bool fillAreas, bool fillLines); + + void addStrokeRegionRef(UINT strokeIndex, TRegion *region); + + int computeRegions(); + void reindexEdges(UINT strokeIndex); + void reindexEdges(const vector &indexes, bool areAdded); + + void checkRegionDbConsistency(); + void cloneRegions(TVectorImage::Imp &out, bool doComputeRegions = true); + + void eraseIntersection(int index); + UINT getFillData(TVectorImage::IntersectionBranch *&v); + void setFillData(TVectorImage::IntersectionBranch *v, UINT branchCount, bool doComputeRegions = true); + void notifyChangedStrokes(const vector &strokeIndexArray, const vector &oldVectorStrokeArray, bool areFlipped); + void insertStrokeAt(VIStroke *stroke, int strokeIndex, bool recomputeRegions = true); + void moveStroke(int fromIndex, int toIndex); + void autoFill(int styleId, bool oddLevel); + + TRegion *getRegion(TRegionId regId, int index) const; + TRegion *getRegionFromLoopStroke(int strokeIndex) const; + VIStroke *joinStroke(int index1, int index2, int cpIndex1, int cpIndex2); + VIStroke *joinStrokeSmoothly(int index1, int index2, int cpIndex1, int cpIndex2); + VIStroke *extendStroke(int index, const TThickPoint &p, int cpIndex); + VIStroke *extendStrokeSmoothly(int index, const TThickPoint &p, int cpIndex); + void removeStrokes(const vector &toBeRemoved, bool deleteThem, bool recomputeRegions); + TStroke *removeStroke(int index, bool doComputeRegions); + void splitStroke(int strokeIndex, const vector &sortedWRanges); + void moveStrokes(int fromIndex, int count, int moveBefore, bool regroup); + + TStroke *removeEndpoints(int strokeIndex); + void restoreEndpoints(int index, TStroke *oldStroke); + + int areDifferentGroup(UINT index1, bool isRegion1, UINT index2, bool isRegion2) const; + void rearrangeMultiGroup(); + void reindexGroups(Imp &img); + void addRegion(TRegion *region); + void regroupGhosts(vector &changedStrokes); + bool inCurrentGroup(int strokeIndex) const; + bool canMoveStrokes(int strokeIndex, int count, int moveBefore) const; +#ifdef _DEBUG + void checkIntersections(); + void checkRegions(const vector ®ions); + void printStrokes(ofstream &os); + void checkGroups(); + +#endif + +#ifdef NEW_REGION_FILL + TRegionFinder *m_regionFinder; + void resetRegionFinder(); +#endif + +private: + void findRegions(const TRectD &rect); + int computeIntersections(); + void findIntersections(); + + int computeEndpointsIntersections(); + //Imp(const TVectorImage::Imp &); + //Imp & operator=(const TVectorImage::Imp &); + void eraseDeadIntersections(); + IntersectedStroke *eraseBranch(Intersection *in, IntersectedStroke *is); + void doEraseIntersection(int index, vector *toBeDeleted = 0); + void eraseEdgeFromStroke(IntersectedStroke *is); + bool areWholeGroups(const vector &indexes) const; + //accorpa tutti i gruppi ghost adiacenti in uno solo, e rinomina gruppi ghost separati con lo stesso id; si usa questa funzione dopo ula creazione di un gruppo o il move di strokes + + //--------------------NUOVO CALCOLO REGIONI------------------------------------------------ + +public: +#ifdef LEVO + vector existingRegions; + TRegion *findRegionFromStroke(const IntersectStroke &stroke, const TPointD &p); + bool findNextStrokes(const TEdge &currEdge, multimap &nextEdges); + bool addNextEdge(TEdge &edge, TRegion ®ion, TRegion *&existingRegion, bool isStartingEdge = false); + bool addNextEdge(TAutocloseEdge &edge, TRegion ®ion, TRegion *&existingRegion); + bool storeRegion(TRegion *region, const TPointD &p); + + bool exploreAndAddNextEdge(TEdge &edge, TEdge &nextEdge, TRegion ®ion, TRegion *&existingRegion); + bool addNextAutocloseEdge(TEdge &edge, TAutocloseEdge &nextEdge, TRegion ®ion, TRegion *&existingRegion); + bool addNextAutocloseEdge(TAutocloseEdge &edge, TEdge &nextEdge, TRegion ®ion, TRegion *&existingRegion); + void computeAutocloseSegments(const TEdge &currEdge, int strokeIndex, multimap &segments); + void computeAutocloseSegmentsSameStroke(const TEdge &currEdge, multimap &segments); +#endif + + //-------------------------------------------------------------------------------------- + +private: + // not implemented + Imp(const Imp &); + Imp &operator=(const Imp &); +}; + +void addRegion(vector ®ionArray, TRegion *region); +//============================================================================= + +#endif diff --git a/toonz/sources/common/tvectorrenderer.cpp b/toonz/sources/common/tvectorrenderer.cpp new file mode 100644 index 0000000..295506f --- /dev/null +++ b/toonz/sources/common/tvectorrenderer.cpp @@ -0,0 +1,459 @@ + + +#ifdef USE_MESA +#include +#endif + +#include "tvectorrenderdata.h" +#include "tvectorimage.h" +#include "tstrokeutil.h" +#include "tmathutil.h" + +#include "tgl.h" +#include "tcurves.h" + +#ifndef __sgi +#include +#include +#include +#else +#include +#include +#include +#endif + +#if defined(LINUX) || defined(__sgi) +#include +#include +#include +#endif + +#ifdef USE_MESA + +extern "C" int GLAPIENTRY wglChoosePixelFormat(HDC, const PIXELFORMATDESCRIPTOR *); +extern "C" int GLAPIENTRY wglDescribePixelFormat(HDC, int, unsigned int, LPPIXELFORMATDESCRIPTOR); +extern "C" int GLAPIENTRY wglSetPixelFormat(HDC, int, const PIXELFORMATDESCRIPTOR *); +extern "C" int GLAPIENTRY wglSwapBuffers(HDC); + +#define ChoosePixelFormat wglChoosePixelFormat +#define SetPixelFormat wglSetPixelFormat +#define SwapBuffers wglSwapBuffers +#endif + +//============================================================================= + +#ifdef WIN32 + +// init a BITMAPINFO structure +void initBITMAPINFO(BITMAPINFO &info, const TRasterP img) +{ + memset(&info, 0, sizeof(BITMAPINFOHEADER)); + + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biWidth = img->getLx(); + info.bmiHeader.biHeight = img->getLy(); + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 32; + info.bmiHeader.biCompression = BI_RGB; + info.bmiHeader.biSizeImage = 0; + info.bmiHeader.biXPelsPerMeter = 1000; + info.bmiHeader.biYPelsPerMeter = 1000; + info.bmiHeader.biClrUsed = 0; + info.bmiHeader.biClrImportant = 0; +} + +//----------------------------------------------------------------------------- +#ifdef BUBU +void hardRenderVectorImage_MESA(const TVectorRenderData &rd, TRaster32P &ras, const TVectorImageP &vimg) +{ + int rasterWidth = ras->getLx(); + int rasterHeight = ras->getLy(); + +//--- begin mesa stuff + +/* Create an RGBA-mode context */ +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + /* specify Z, stencil, accum sizes */ + OSMesaContext ctx = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, NULL); +#else + OSMesaContext ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); +#endif + if (!ctx) { + throw TException("OSMesaCreateContext failed!\n"); + } + ras->lock(); + /* Bind the buffer to the context and make it current */ + if (!OSMesaMakeCurrent(ctx, ras->getRawData(), GL_UNSIGNED_BYTE, ras->getLx(), ras->getLy())) { + { + ras->unlock(); + throw TException("OSMesaMakeCurrent failed!\n"); + } + } + + //---end mesa stuff + // cfr. help: OpenGL/Programming tip/OpenGL Correctness Tips + glViewport(0, 0, rasterWidth, rasterHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, rasterWidth, 0, rasterHeight); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); + /* + glClearColor(0.0f,0.0f,0.0f,0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // draw background + glRasterPos2d(0, 0); + glDrawPixels( ras->getLx(),ras->getLy(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, ras->getRawData()); +*/ + // do OpenGL draw + assert(vimg); + tglDraw(rd, vimg.getPointer()); + + // force to finish + glFlush(); + glFinish(); + /* + // set info in out raster + TDimension size = ras->getSize(); + + if( ras->getWrap() == rasterWidth ) + memcpy( ras->getRawData(), offData, ras->getPixelSize()*size.lx*size.ly ); + else + { + TRaster32P temp( ras->getLx(), ras->getLy()); + memcpy( temp->getRawData(), offData, ras->getPixelSize()*size.lx*size.ly ); + ras->copy(temp); + } +*/ + OSMesaDestroyContext(ctx); + ras->unlock(); +} +#endif //BUBU +void hardRenderVectorImage(const TVectorRenderData &rd, TRaster32P &ras, const TVectorImageP &vimg) +{ + int rasterWidth = ras->getLx(); + int rasterHeight = ras->getLy(); + ras->lock(); + +#ifdef USE_MESA +/* Create an RGBA-mode context */ +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + /* specify Z, stencil, accum sizes */ + OSMesaContext ctx = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, NULL); +#else + OSMesaContext ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); +#endif + if (!ctx) { + ras->unlock(); + throw TException("OSMesaCreateContext failed!\n"); + } + + /* Bind the buffer to the context and make it current */ + + if (!OSMesaMakeCurrent(ctx, ras->getRawData(), GL_UNSIGNED_BYTE, ras->getLx(), ras->getLy())) { + + { + ras->unlock(); + throw TException("OSMesaMakeCurrent failed!\n"); + } + } +#else + BITMAPINFO info; + + initBITMAPINFO(info, ras); + + void *offData = 0; // a pointer to buffer + + // open an offscreen device + HDC offDC = CreateCompatibleDC(NULL); + + HBITMAP offDIB = // and a bitmap image + CreateDIBSection(offDC, &info, DIB_RGB_COLORS, &offData, NULL, 0); + + assert(offDIB); + assert(offData); + + int dataSize = // number of byte of raster + rasterWidth * rasterHeight * 4; + + memset(offData, 0, dataSize); + + HGDIOBJ oldobj = // select BIB to write + SelectObject(offDC, offDIB); + + static PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + 0 | (false ? (PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER) : (PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI)) | PFD_SUPPORT_OPENGL, // support OpenGL + PFD_TYPE_RGBA, // RGBA type + 32, // 32-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + + // get the best available match of pixel format for the device context + int iPixelFormat = ChoosePixelFormat(offDC, &pfd); + assert(iPixelFormat != 0); + + // make that the pixel format of the device context + int ret = SetPixelFormat(offDC, iPixelFormat, &pfd); + assert(ret == TRUE); + + // make a valid context for OpenGL rendering + HGLRC hglRC = wglCreateContext(offDC); + assert(hglRC); + ret = wglMakeCurrent(offDC, hglRC); + assert(ret == TRUE); +#endif + // cfr. help: OpenGL/Programming tip/OpenGL Correctness Tips + glViewport(0, 0, rasterWidth, rasterHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, rasterWidth, 0, rasterHeight); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); +#ifndef USE_MESA + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // draw background + glRasterPos2d(0, 0); + glDrawPixels(ras->getLx(), ras->getLy(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, ras->getRawData()); +#endif + // do OpenGL draw + assert(vimg); + + tglDraw(rd, vimg.getPointer()); + + // force to finish + glFlush(); + +#ifdef USE_MESA + OSMesaDestroyContext(ctx); +#else + // set info in out raster + TDimension size = ras->getSize(); + + if (ras->getWrap() == rasterWidth) + memcpy(ras->getRawData(), offData, ras->getPixelSize() * size.lx * size.ly); + else { + TRaster32P temp(ras->getLx(), ras->getLy()); + memcpy(temp->getRawData(), offData, ras->getPixelSize() * size.lx * size.ly); + ras->copy(temp); + } + + ret = wglMakeCurrent(offDC, NULL); + assert(ret == TRUE); + wglDeleteContext(hglRC); + + // release object + SelectObject(offDC, oldobj); + DeleteObject(offDIB); + DeleteObject(offDC); +#endif + ras->unlock(); +} + +// end of WIN32 +#elif defined(__sgi) || defined(LINUX) + +//============================================================================= + +namespace +{ + +GLXContext ctx; +XVisualInfo *visinfo; +GC gc; +Window make_rgb_window(Display *dpy, + unsigned int width, unsigned int height) +{ + const int sbAttrib[] = {GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + None}; + const int dbAttrib[] = {GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + None}; + int scrnum; + XSetWindowAttributes attr; + TUINT32 mask; + Window root; + Window win; + + scrnum = DefaultScreen(dpy); + root = RootWindow(dpy, scrnum); + + visinfo = glXChooseVisual(dpy, scrnum, (int *)sbAttrib); + if (!visinfo) { + visinfo = glXChooseVisual(dpy, scrnum, (int *)dbAttrib); + if (!visinfo) { + printf("Error: couldn't get an RGB visual\n"); + exit(1); + } + } + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + /* TODO: share root colormap if possible */ + attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask | ExposureMask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + win = XCreateWindow(dpy, root, 0, 0, width, height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr); + + /* make an X GC so we can do XCopyArea later */ + gc = XCreateGC(dpy, win, 0, NULL); + + /* need indirect context */ + ctx = glXCreateContext(dpy, visinfo, NULL, False); + if (!ctx) { + printf("Error: glXCreateContext failed\n"); + exit(-1); + } + + printf("Direct rendering: %s\n", glXIsDirect(dpy, ctx) ? "Yes" : "No"); + + return win; +} + +GLXPixmap make_pixmap(Display *dpy, Window win, + unsigned int width, unsigned int height, + Pixmap *pixmap) +{ + Pixmap pm; + GLXPixmap glxpm; + XWindowAttributes attr; + + pm = XCreatePixmap(dpy, win, width, height, visinfo->depth); + if (!pm) { + printf("Error: XCreatePixmap failed\n"); + exit(-1); + } + + XGetWindowAttributes(dpy, win, &attr); + +/* + * IMPORTANT: + * Use the glXCreateGLXPixmapMESA funtion when using Mesa because + * Mesa needs to know the colormap associated with a pixmap in order + * to render correctly. This is because Mesa allows RGB rendering + * into any kind of visual, not just TrueColor or DirectColor. + */ +#ifdef PROBLEMI_CON_IL_DRIVER_NVIDIA // GLX_MESA_pixmap_colormap // + if (strstr(glXQueryExtensionsString(dpy, 0), "GLX_MESA_pixmap_colormap")) { + /* stand-alone Mesa, specify the colormap */ + glxpm = glXCreateGLXPixmapMESA(dpy, visinfo, pm, attr.colormap); + } else { + glxpm = glXCreateGLXPixmap(dpy, visinfo, pm); + } +#else + /* This will work with Mesa too if the visual is TrueColor or DirectColor */ + glxpm = glXCreateGLXPixmap(dpy, visinfo, pm); +#endif + + if (!glxpm) { + printf("Error: GLXCreateGLXPixmap failed\n"); + exit(-1); + } + + *pixmap = pm; + + return glxpm; +} +} + +//void offscreenRender(TRaster32P& ras, const TVectorImageP& vimg, const TAffine& aff) +void hardRenderVectorImage(const TVectorRenderData &rd, TRaster32P &ras, const TVectorImageP &vimg) +{ + Display *dpy; + Window win; + Pixmap pm; + GLXPixmap glxpm; + + ras->lock(); + + dpy = XOpenDisplay(NULL); + + win = make_rgb_window(dpy, ras->getLx(), ras->getLy()); + glxpm = make_pixmap(dpy, win, ras->getLx(), ras->getLy(), &pm); + + GLXContext oldctx = glXGetCurrentContext(); + GLXDrawable olddrw = glXGetCurrentDrawable(); + + glXMakeCurrent(dpy, glxpm, ctx); + //printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); + + // cfr. help: OpenGL/Programming tip/OpenGL Correctness Tips + glViewport(0, 0, ras->getLx(), ras->getLy()); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, ras->getLx(), 0, ras->getLy()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // draw background + glRasterPos2d(0, 0); + glDrawPixels(ras->getLx(), ras->getLy(), GL_RGBA, GL_UNSIGNED_BYTE, ras->getRawData()); + + // do OpenGL draw + assert(vimg); + + tglDraw(rd, vimg.getPointer()); + + glFlush(); + +#if defined(__sgi) + + glReadPixels(0, 0, + ras->getLx(), ras->getLy(), + GL_ABGR_EXT, + GL_UNSIGNED_BYTE, + ras->getRawData()); + +#elif defined(LINUX) + + glReadPixels(0, 0, + ras->getLx(), ras->getLy(), + GL_RGBA, + GL_UNSIGNED_BYTE, + ras->getRawData()); +#endif + + Bool ret = glXMakeCurrent(dpy, olddrw, oldctx); +#ifdef DEBUG + if (!ret) { + std::cerr << __FUNCTION__ + << " error in glXMakeCurrent olddrw=" << olddrw + << " oldctx=" << oldctx << std::endl; + } +#endif + ras->unlock(); +} + +#endif diff --git a/toonz/sources/common/tvrender/macofflinegl.cpp b/toonz/sources/common/tvrender/macofflinegl.cpp new file mode 100644 index 0000000..2a2f793 --- /dev/null +++ b/toonz/sources/common/tvrender/macofflinegl.cpp @@ -0,0 +1,198 @@ + + +#include "macofflinegl.h" +#include +#include + +#include "tthread.h" + +namespace +{ + +#if defined(powerpc) +void rightRotateBits(UCHAR *buf, int bufferSize) +{ + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = app >> 8 | app << 24; + } +} +#else +void rightRotateBits(UCHAR *buf, int bufferSize) +{ + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = (app >> 16 & 0x000000ff) | (app << 16 & 0x00ff0000) | (app & 0xff00ff00); + } +} +#endif + +} // namespace + +GLubyte *memBuffer; + +//============================================================================= +// MacOfflineGL : implem. offlineGL usando Pixel Buffer (tramite AGL) +//----------------------------------------------------------------------------- + +MacOfflineGL::MacOfflineGL(TDimension rasterSize, const TOfflineGL::Imp *shared) + : TOfflineGL::Imp(rasterSize.lx, rasterSize.ly), m_context(0), m_oldContext(0) +{ + createContext(rasterSize, shared); +} + +//----------------------------------------------------------------------------- + +MacOfflineGL::~MacOfflineGL() +{ + aglDestroyContext(m_context); +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::createContext(TDimension rasterSize, const TOfflineGL::Imp *shared) +{ + + GLint attribs[20], cnt = 0; + + //NOTE: AGL_OFFSCREEN *must* be selected - or it seems that gl surfaces are never destructed correctly! + //This may lead to a kernel panic! + + attribs[cnt++] = AGL_RGBA; + attribs[cnt++] = GL_TRUE; + attribs[cnt++] = AGL_PIXEL_SIZE; + attribs[cnt++] = 32; + attribs[cnt++] = AGL_BUFFER_SIZE; + attribs[cnt++] = 32; + attribs[cnt++] = AGL_STENCIL_SIZE; + attribs[cnt++] = 8; + attribs[cnt++] = AGL_DEPTH_SIZE; + attribs[cnt++] = 24; + attribs[cnt++] = AGL_OFFSCREEN; + attribs[cnt++] = AGL_ALPHA_SIZE; + attribs[cnt++] = 8; + attribs[cnt] = AGL_NONE; + + AGLPixelFormat fmt = aglChoosePixelFormat(0, 0, attribs); + + if (fmt == NULL) { + GLenum err = aglGetError(); + std::cout << "Unable to create a pixel format, AGLError = " << err << std::endl; + } + + m_context = aglCreateContext(fmt, NULL); + if (!m_context) { + GLenum err = aglGetError(); + /* + AGL_NO_ERROR 0 + AGL_BAD_ATTRIBUTE 10000 + AGL_BAD_PROPERTY 10001 + AGL_BAD_PIXELFMT 10002 + AGL_BAD_RENDINFO 10003 + AGL_BAD_CONTEXT 10004 + AGL_BAD_DRAWABLE 10005 + AGL_BAD_GDEV 10006 + AGL_BAD_STATE 10007 + AGL_BAD_VALUE 10008 + AGL_BAD_MATCH 10009 + AGL_BAD_ENUM 10010 + AGL_BAD_OFFSCREEN 10011 + AGL_BAD_FULLSCREEN 10012 + AGL_BAD_WINDOW 10013 + AGL_BAD_POINTER 10014 + AGL_BAD_MODULE 10015 + AGL_BAD_ALLOC 10016 + AGL_BAD_CONNECTION 10017 + */ + std::cout << "Unable to create an OpenGL Context, AGLError = " << err << std::endl; + } + + makeCurrent(); + + // Creo il pixel buffer + + GLboolean ret; + + AGLPbuffer pbuffer; + + ret = aglCreatePBuffer(rasterSize.lx, rasterSize.ly, GL_TEXTURE_RECTANGLE_EXT, GL_RGBA, 0, &pbuffer); + if (!ret) { + GLenum err = aglGetError(); + std::cout << "Unable to create a PBuffer, AGLError = " << err << std::endl; + } + + //memBuffer = new GLubyte[rasterSize.lx*rasterSize.ly*4]; + + ret = aglSetOffScreen(m_context, rasterSize.lx, rasterSize.ly, rasterSize.lx * 4, memBuffer); + + ret = aglSetPBuffer(m_context, pbuffer, 0, 0, 0); + if (!ret) { + GLenum err = aglGetError(); + std::cout << "Unable to set a PBuffer, AGLError = " << err << std::endl; + } + + // Non serve piu' + aglDestroyPixelFormat(fmt); +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::makeCurrent() +{ + if (m_context) { + bool ret = aglSetCurrentContext(m_context); + if (ret == GL_FALSE) { + GLenum err = aglGetError(); + std::cout << "Unable to set current OpenGL Context, AGLError = " << err << std::endl; + } + } else + m_oldContext = 0; +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::doneCurrent() +{ + if (aglGetCurrentContext() != m_context) + return; + aglSetCurrentContext(0); +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::saveCurrentContext() +{ + m_oldContext = aglGetCurrentContext(); +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::restoreCurrentContext() +{ + if (m_oldContext) + aglSetCurrentContext(m_oldContext); + m_oldContext = 0; +} + +//----------------------------------------------------------------------------- + +void MacOfflineGL::getRaster(TRaster32P raster) +{ + makeCurrent(); + glFinish(); + + int lx = raster->getLx(); + int ly = raster->getLy(); + + raster->lock(); + + glReadPixels(0, 0, lx, ly, GL_RGBA, GL_UNSIGNED_BYTE, raster->getRawData()); + + rightRotateBits(raster->getRawData(), lx * ly); + + raster->unlock(); +} diff --git a/toonz/sources/common/tvrender/qtofflinegl.cpp b/toonz/sources/common/tvrender/qtofflinegl.cpp new file mode 100644 index 0000000..0793b00 --- /dev/null +++ b/toonz/sources/common/tvrender/qtofflinegl.cpp @@ -0,0 +1,377 @@ + + +#include "qtofflinegl.h" +#include +#include + +//----------------------------------------------------------------------------- + +#ifdef WIN32 + +void swapRedBlueChannels(void *buffer, int bufferSize) // Flips The Red And Blue Bytes (WidthxHeight) +{ + void *b = buffer; // Pointer To The Buffer + +#ifdef x64 + int size = bufferSize; + UCHAR *pix = (UCHAR *)b; + while (size > 0) { + UCHAR r = *pix; + UCHAR b = *(pix + 2); + *pix = b; + *(pix + 2) = r; + pix += 4; + size--; + } +#else + __asm // Assembler Code To Follow + { + mov ecx, bufferSize // Counter Set To Dimensions Of Our Memory Block + mov ebx, b // Points ebx To Our Data (b) + label: // Label Used For Looping + mov al,[ebx+0] // Loads Value At ebx Into al + mov ah,[ebx+2] // Loads Value At ebx+2 Into ah + mov [ebx+2],al // Stores Value In al At ebx+2 + mov [ebx+0],ah // Stores Value In ah At ebx + + add ebx,4 // Moves Through The Data By 4 Bytes + dec ecx // Decreases Our Loop Counter + jnz label // If Not Zero Jump Back To Label + } +#endif +} + +#endif + +//----------------------------------------------------------------------------- + +#if defined(MACOSX) +#if defined(powerpc) + +void rightRotateBits(UCHAR *buf, int bufferSize) +{ + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = app >> 8 | app << 24; + } +} +#else +void rightRotateBits(UCHAR *buf, int bufferSize) +{ + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = (app >> 16 & 0x000000ff) | (app << 16 & 0x00ff0000) | (app & 0xff00ff00); + } +} +#endif +#endif + +//----------------------------------------------------------------------------- + +//============================================================================= +// QtOfflineGL : implem. offlineGL usando QT +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +QtOfflineGL::QtOfflineGL(TDimension rasterSize, const TOfflineGL::Imp *shared) + : TOfflineGL::Imp(rasterSize.lx, rasterSize.ly), m_context(0), m_oldContext(0) +{ + createContext(rasterSize, shared); + /* + makeCurrent(); + + glClearColor(0.0f,0.0f,0.0f,0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + doneCurrent(); +*/ +} + +//----------------------------------------------------------------------------- + +QtOfflineGL::~QtOfflineGL() +{ + delete m_context; +} + +//----------------------------------------------------------------------------- + +void QtOfflineGL::createContext(TDimension rasterSize, const TOfflineGL::Imp *shared) +{ + // Imposto il formato dei Pixel (pixelFormat) + /* + 32, // 32-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 8, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 32, // max stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + + ATTENZIONE !! SU MAC IL FORMATO E' DIVERSO (casomai possiamo mettere un ifdef) + + SPECIFICHE MAC = depth_size 24, stencil_size 8, alpha_size 1 + + */ + + QGLFormat fmt; + +#ifdef WIN32 + fmt.setAlphaBufferSize(8); + fmt.setAlpha(true); + fmt.setRgba(true); + fmt.setDepthBufferSize(32); + fmt.setDepth(true); + fmt.setStencilBufferSize(32); + fmt.setStencil(true); + fmt.setAccum(false); + fmt.setPlane(0); +#elif MACOSX + fmt = QGLFormat::defaultFormat(); + //printf("GL Version: %s\n",glGetString(GL_VERSION)); + fmt.setVersion(2, 1); /* OSX10.8 では 3.2 だめかも */ +#if 0 + fmt.setAlphaBufferSize(8); + fmt.setAlpha(true); + fmt.setRgba(true); + fmt.setDepthBufferSize(32); + fmt.setDepth(true); + fmt.setStencilBufferSize(8); + fmt.setStencil(true); + fmt.setAccum(false); + fmt.setPlane(0); + fmt.setDirectRendering(false); +#endif +#endif + /* FIXME: ここでいう QPixmap は Level Strip のセルに相当する. + QPixmap に GLContext を生成して bind できれば描画したベクタのラスタ画像がそこに反映されるはずだが + QLContext の生成ができないためにうまくいかない. + */ + printf("QPixmap(%d, %d)\n", rasterSize.lx, rasterSize.ly); + //QPixmap *m_pixmap = new QPixmap(rasterSize.lx, rasterSize.ly); + + // Inizializzo un contesto openGL utilizzando una QPixmap + + m_context = new QOpenGLContext(); + //m_context = new QGLContext(fmt); + + m_surface = new QOffscreenSurface(); + m_surface->setFormat(m_context->format()); + //QSurfaceFormat sfmt = QGuiApplication::focusWindow()->format(); + m_surface->create(); + + printf("create context:%p [thread:0x%x]\n", m_context, QThread::currentThreadId()); + //m_context->setFormat(sfmt); + + // Creo il contesto OpenGL - assicurandomi che sia effettivamente creato + // NOTA: Se il contesto non viene creato, di solito basta ritentare qualche volta. + bool ret = m_context->create(); +} +//----------------------------------------------------------------------------- + +void QtOfflineGL::makeCurrent() +{ + if (m_context) { + m_context->makeCurrent(m_surface); + } + // else + // m_oldContext = 0; +} + +//----------------------------------------------------------------------------- + +void QtOfflineGL::doneCurrent() +{ + if (m_context) { + m_context->doneCurrent(); + } +} + +//----------------------------------------------------------------------------- + +void QtOfflineGL::saveCurrentContext() +{ + // m_oldContext = const_cast(QGLContext::currentContext()); +} + +//----------------------------------------------------------------------------- + +void QtOfflineGL::restoreCurrentContext() +{ + // if(m_oldContext) m_oldContext->makeCurrent(); + // m_oldContext = 0; +} + +//----------------------------------------------------------------------------- + +void QtOfflineGL::getRaster(TRaster32P raster) +{ + makeCurrent(); + glFlush(); + + int lx = raster->getLx(); + int ly = raster->getLy(); + + raster->lock(); + glReadPixels(0, 0, lx, ly, + GL_RGBA /*GL_BGRA_EXT*/, GL_UNSIGNED_BYTE, + raster->getRawData()); + +#ifdef WIN32 + swapRedBlueChannels(raster->getRawData(), lx * ly); +#elif MACOSX + rightRotateBits(raster->getRawData(), lx * ly); +#endif + raster->unlock(); +} + +//QGLPixelBuffer::hasOpenGLPbuffers() (statica) -> true se la scheda supporta i PBuffer + +//============================================================================= +// QtOfflineGLPBuffer : implem. offlineGL usando QT e PBuffer +//----------------------------------------------------------------------------- + +QtOfflineGLPBuffer::QtOfflineGLPBuffer(TDimension rasterSize) + : TOfflineGL::Imp(rasterSize.lx, rasterSize.ly), m_context(0) +{ + createContext(rasterSize); + + makeCurrent(); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + doneCurrent(); +} + +//----------------------------------------------------------------------------- + +QtOfflineGLPBuffer::~QtOfflineGLPBuffer() +{ + delete m_context; +} + +//----------------------------------------------------------------------------- + +void QtOfflineGLPBuffer::createContext(TDimension rasterSize) +{ + // Imposto il formato dei Pixel (pixelFormat) + /* + 32, // 32-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 8, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 32, // max stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + + ATTENZIONE !! SU MAC IL FORMATO E' DIVERSO (casomai possiamo mettere un ifdef) + + SPECIFICHE MAC = depth_size 24, stencil_size 8, alpha_size 1 + + */ + + QGLFormat fmt; + +#ifdef WIN32 + fmt.setAlphaBufferSize(8); + fmt.setAlpha(false); + fmt.setRgba(true); + fmt.setDepthBufferSize(32); + fmt.setDepth(true); + fmt.setStencilBufferSize(32); + fmt.setStencil(true); + fmt.setAccum(false); + fmt.setPlane(0); +#elif MACOSX + fmt.setAlphaBufferSize(1); + fmt.setAlpha(false); + fmt.setRgba(true); + fmt.setDepthBufferSize(24); + fmt.setDepth(true); + fmt.setStencilBufferSize(8); + fmt.setStencil(true); + fmt.setAccum(false); + fmt.setPlane(0); +#endif + + // Il PixelBuffer deve essere con width ed height potenze di 2 + + int sizeMax = tmax(rasterSize.lx, rasterSize.ly); + + // trovo la potenza di 2 che "contiene" sizeMax e la utilizzo per il PBuffer + int pBufferSize = 2; + while (pBufferSize < sizeMax) + pBufferSize *= 2; + + m_context = new QGLPixelBuffer(QSize(pBufferSize, pBufferSize), fmt); +} + +//----------------------------------------------------------------------------- + +void QtOfflineGLPBuffer::makeCurrent() +{ + if (m_context) + m_context->makeCurrent(); +} + +//----------------------------------------------------------------------------- + +void QtOfflineGLPBuffer::doneCurrent() +{ + if (m_context) + m_context->doneCurrent(); +} + +//----------------------------------------------------------------------------- + +void QtOfflineGLPBuffer::getRaster(TRaster32P raster) +{ + makeCurrent(); + glFlush(); + + //The image is stored using a 32-bit ARGB format (0xAARRGGBB). + QImage image = m_context->toImage(); + + int lx = raster->getLx(), ly = raster->getLy(); + + static const TRaster32P emptyRaster; + if (image.height() == 0 || image.width() == 0) + return; + + // devo iniziare a leggere la Y da un certo offset + // dato dalla differenza tra la y della image e la y del raster + int yOffset = image.height() - ly; + raster->lock(); + + for (int y = 0; y < ly; y++) { + QRgb *inpPix = (QRgb *)image.scanLine(yOffset + y); + + TPixel32 *pix = raster->pixels(ly - 1 - y); + TPixel32 *endPix = pix + lx; + + for (; pix < endPix; ++pix) { + pix->m = 255; + pix->r = qRed(*inpPix); + pix->g = qGreen(*inpPix); + pix->b = qBlue(*inpPix); + *inpPix++; + } + } + raster->unlock(); +} diff --git a/toonz/sources/common/tvrender/tcolorstyles.cpp b/toonz/sources/common/tvrender/tcolorstyles.cpp new file mode 100644 index 0000000..ffdad0b --- /dev/null +++ b/toonz/sources/common/tvrender/tcolorstyles.cpp @@ -0,0 +1,626 @@ + + +// TnzCore includes +#include "tpalette.h" +#include "tstroke.h" +#include "tvectorimage.h" +#include "texception.h" +#include "tvectorrenderdata.h" +#include "tconvert.h" +#include "tofflinegl.h" +#include "tpixelutils.h" +#include "tflash.h" +#include "tcolorstyles.h" + +//***************************************************************************** +// Macros +//***************************************************************************** + +#ifndef checkErrorsByGL +#define checkErrorsByGL \ + { \ + GLenum err = glGetError(); \ + assert(err != GL_INVALID_ENUM); \ + assert(err != GL_INVALID_VALUE); \ + assert(err != GL_INVALID_OPERATION); \ + assert(err != GL_STACK_OVERFLOW); \ + assert(err != GL_STACK_UNDERFLOW); \ + assert(err != GL_OUT_OF_MEMORY); \ + assert(err == GL_NO_ERROR); \ + } +#endif + +#undef checkErrorsByGL +#define checkErrorsByGL + +//***************************************************************************** +// TColorStyle implementation +//***************************************************************************** + +int TColorStyle::m_currentFrame = 0; + +//------------------------------------------------------------------- + +TColorStyle::TColorStyle() + : m_name(L"color"), m_globalName(L""), m_originalName(L""), m_versionNumber(0), m_flags(0), m_enabled(true), m_icon(0), m_validIcon(false), m_isEditedFromOriginal(false) +{ +} + +//------------------------------------------------------------------- + +TColorStyle::~TColorStyle() +{ +} + +//------------------------------------------------------------------- + +TColorStyle::TColorStyle(const TColorStyle &other) + : m_name(other.m_name), m_globalName(other.m_globalName), m_originalName(other.m_originalName), m_versionNumber(other.m_versionNumber), m_flags(other.m_flags), m_enabled(other.m_enabled), m_validIcon(false), m_isEditedFromOriginal(other.m_isEditedFromOriginal) +{ +} + +//------------------------------------------------------------------- + +TColorStyle &TColorStyle::operator=(const TColorStyle &other) +{ + m_name = other.m_name; + m_globalName = other.m_globalName; + m_originalName = other.m_originalName; + m_versionNumber = other.m_versionNumber; + m_flags = other.m_flags; + m_enabled = other.m_enabled; + m_validIcon = false; + m_isEditedFromOriginal = other.m_isEditedFromOriginal; + + return *this; +} + +//------------------------------------------------------------------- + +bool TColorStyle::operator==(const TColorStyle &cs) const +{ + if (getTagId() != cs.getTagId()) + return false; + + if (getMainColor() != cs.getMainColor()) + return false; + + int paramCount = getParamCount(); + if (paramCount != cs.getParamCount()) + return false; + + int colorParamCount = getColorParamCount(); + if (colorParamCount != cs.getColorParamCount()) + return false; + + if (m_name != cs.getName()) + return false; + if (m_originalName != cs.getOriginalName()) + return false; + if (m_globalName != cs.getGlobalName()) + return false; + if (m_isEditedFromOriginal != cs.getIsEditedFlag()) + return false; + + for (int p = 0; p < colorParamCount; ++p) + if (getColorParamValue(p) != cs.getColorParamValue(p)) + return false; + + for (int p = 0; p < paramCount; ++p) { + switch (getParamType(p)) { + case BOOL: + if (getParamValue(bool_tag(), p) != cs.getParamValue(bool_tag(), p)) + return false; + + CASE INT : case ENUM : if (getParamValue(int_tag(), p) != cs.getParamValue(int_tag(), p)) return false; + + CASE DOUBLE : if (getParamValue(double_tag(), p) != cs.getParamValue(double_tag(), p)) return false; + + CASE FILEPATH : if (getParamValue(TFilePath_tag(), p) != cs.getParamValue(TFilePath_tag(), p)) return false; + + DEFAULT: + assert(false); + } + } + + return true; +} + +//------------------------------------------------------------------- + +QString TColorStyle::getParamNames(int index) const +{ + assert(false); + return QString(""); +} + +//------------------------------------------------------------------- + +void TColorStyle::updateVersionNumber() +{ + ++m_versionNumber; +} + +//------------------------------------------------------------------- + +const TRaster32P &TColorStyle::getIcon(const TDimension &d) +{ + checkErrorsByGL; + if (!m_validIcon || !m_icon || m_icon->getSize() != d) { + checkErrorsByGL; + makeIcon(d); + checkErrorsByGL; + m_validIcon = true; + } + checkErrorsByGL; + + if (!m_icon) { + checkErrorsByGL; + TRaster32P icon(d); + checkErrorsByGL; + icon->fill(TPixel32::Black); + checkErrorsByGL; + int lx = icon->getLx(); + checkErrorsByGL; + int ly = icon->getLy(); + checkErrorsByGL; + for (int y = 0; y < ly; y++) { + checkErrorsByGL; + int x = ((lx - 1 - 10) * y / ly); + checkErrorsByGL; + icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red); + checkErrorsByGL; + } + checkErrorsByGL; + m_icon = icon; + checkErrorsByGL; + } + return m_icon; +} + +//------------------------------------------------------------------- + +void TColorStyle::makeIcon(const TDimension &d) +{ + checkErrorsByGL; + TColorStyle *style = this->clone(); + checkErrorsByGL; + + TPaletteP tmpPalette = new TPalette(); + checkErrorsByGL; + int id = tmpPalette->addStyle(style); + checkErrorsByGL; + + int contextLx = pow(2.0, tceil(log((double)d.lx) / log(2.0))); + int contextLy = pow(2.0, tceil(log((double)d.ly) / log(2.0))); + TDimension dim(contextLx, contextLy); + + TOfflineGL *glContext = TOfflineGL::getStock(dim); + + checkErrorsByGL; + glContext->clear(TPixel32::White); + checkErrorsByGL; + + TVectorImageP img = new TVectorImage; + checkErrorsByGL; + img->setPalette(tmpPalette.getPointer()); + checkErrorsByGL; + + vector points(3); + + if (isRegionStyle() && !isStrokeStyle()) { + points[0] = TThickPoint(-55, -50, 1); + points[1] = TThickPoint(0, -60, 1); + points[2] = TThickPoint(55, -50, 1); + TStroke *stroke1 = new TStroke(points); + + img->addStroke(stroke1); + + points[0] = TThickPoint(50, -55, 1); + points[1] = TThickPoint(60, 0, 1); + points[2] = TThickPoint(50, 55, 1); + TStroke *stroke2 = new TStroke(points); + img->addStroke(stroke2); + + points[0] = TThickPoint(55, 50, 1); + points[1] = TThickPoint(0, 60, 1); + points[2] = TThickPoint(-55, 50, 1); + TStroke *stroke3 = new TStroke(points); + img->addStroke(stroke3); + + points[0] = TThickPoint(-50, 55, 1); + points[1] = TThickPoint(-60, 0, 1); + points[2] = TThickPoint(-50, -55, 1); + TStroke *stroke4 = new TStroke(points); + img->addStroke(stroke4); + + img->fill(TPointD(0, 0), id); + } else if (isStrokeStyle() && !isRegionStyle()) { + + double rasX05 = d.lx * 0.5; + double rasY05 = d.ly * 0.5; + + points[0] = TThickPoint(-rasX05, -rasY05, 7); + points[1] = TThickPoint(0, -rasY05, 9); + points[2] = TThickPoint(rasX05, rasY05, 12); + TStroke *stroke1 = new TStroke(points); + + stroke1->setStyle(id); + + img->addStroke(stroke1); + points.clear(); + } else if (!isRasterStyle()) { + assert(isStrokeStyle() && isRegionStyle()); + + points[0] = TThickPoint(-60, -30, 0.5); + points[1] = TThickPoint(0, -30, 0.5); + points[2] = TThickPoint(60, -30, 0.5); + TStroke *stroke1 = new TStroke(points); + stroke1->setStyle(id); + img->addStroke(stroke1); + + points[0] = TThickPoint(60, -30, 0.5); + points[1] = TThickPoint(60, 0, 0.5); + points[2] = TThickPoint(60, 30, 0.5); + TStroke *stroke2 = new TStroke(points); + stroke2->setStyle(id); + img->addStroke(stroke2); + + points[0] = TThickPoint(60, 30, 0.5); + points[1] = TThickPoint(0, 30, 0.5); + points[2] = TThickPoint(-60, 30, 0.5); + TStroke *stroke3 = new TStroke(points); + stroke3->setStyle(id); + img->addStroke(stroke3); + + points[0] = TThickPoint(-60, 30, 0.5); + points[1] = TThickPoint(-60, 0, 0.5); + points[2] = TThickPoint(-60, -30, 0.5); + TStroke *stroke4 = new TStroke(points); + stroke4->setStyle(id); + img->addStroke(stroke4); + + img->fill(TPointD(0, 0), id); + } + + TRectD bbox = img->getBBox(); + checkErrorsByGL; + + bbox = bbox.enlarge(TDimensionD(-10, -10)); + checkErrorsByGL; + + double scx = 0.9 * d.lx / bbox.getLx(); + double scy = 0.9 * d.ly / bbox.getLy(); + double sc = tmin(scx, scy); + double dx = (d.lx - bbox.getLx() * sc) * 0.5; + double dy = (d.ly - bbox.getLy() * sc) * 0.5; + TAffine aff = TScale(scx, scy) * TTranslation(-bbox.getP00() + TPointD(dx, dy)); + + checkErrorsByGL; + if (isRegionStyle() && !isStrokeStyle()) + aff = aff * TTranslation(-10, -10); + + checkErrorsByGL; + const TVectorRenderData rd(aff, TRect(), tmpPalette.getPointer(), 0, true); + checkErrorsByGL; + glContext->draw(img, rd); + checkErrorsByGL; + + TRect rect(d); + if (!m_icon || m_icon->getSize() != d) { + checkErrorsByGL; + m_icon = glContext->getRaster()->extract(rect)->clone(); + } else { + checkErrorsByGL; + m_icon->copy(glContext->getRaster()->extract(rect)); + } +} + +//------------------------------------------------------------------- + +void TColorStyle::assignNames(const TColorStyle *src) +{ + m_name = src->getName(); + m_globalName = src->getGlobalName(); + m_originalName = src->getOriginalName(); + m_isEditedFromOriginal = src->getIsEditedFlag(); +} + +//------------------------------------------------------------------- + +void TColorStyle::assignBlend(const TColorStyle &a, const TColorStyle &b, double t) +{ + // Blend colors + { + int col, colCount = getColorParamCount(); + assert(a.getColorParamCount() == colCount && b.getColorParamCount() == colCount); + + for (col = 0; col != colCount; ++col) + setColorParamValue(col, blend(a.getColorParamValue(col), b.getColorParamValue(col), t)); + } + + // Blend parameters + { + int par, parCount = getParamCount(); + assert(a.getParamCount() == parCount && b.getParamCount() == parCount); + + for (par = 0; par != parCount; ++par) { + switch (getParamType(par)) { + CASE DOUBLE : setParamValue(par, (1 - t) * a.getParamValue(double_tag(), par) + t * b.getParamValue(double_tag(), par)); + + DEFAULT:; + } + } + } + + invalidateIcon(); +} + +//=================================================================== +// +// color style global list +// +//=================================================================== + +namespace +{ + +class ColorStyleList +{ // singleton + ColorStyleList() {} + + struct Item { + TColorStyle *m_style; + bool m_isObsolete; + // Item() : m_style(0), m_isObsolete(false) { assert(0); } + Item(TColorStyle *style, bool obsolete = false) + : m_style(style), m_isObsolete(obsolete) {} + }; + + typedef std::map Table; + Table m_table; + +public: + static ColorStyleList *instance() + { + static ColorStyleList *_instance = 0; + if (!_instance) + _instance = new ColorStyleList(); + return _instance; + } + + int getStyleCount() + { + return int(m_table.size()); + } + + void declare(TColorStyle *style) + { + int id = style->getTagId(); + if (m_table.find(id) != m_table.end()) { + throw TException("Duplicate color style declaration. id = " + toString(id)); + } + m_table.insert(std::make_pair(id, Item(style))); + vector ids; + style->getObsoleteTagIds(ids); + for (std::vector::iterator it = ids.begin(); it != ids.end(); ++it) { + if (m_table.find(*it) != m_table.end()) { + throw TException("Duplicate color style declaration for obsolete style. id = " + toString(*it)); + } + m_table.insert(std::make_pair(*it, Item(style->clone(), true))); + } + } + + TColorStyle *create(int id, bool &isObsolete) + { + Table::iterator it = m_table.find(id); + if (it == m_table.end()) + throw TException("Unknown color style id; id = " + toString(id)); + + isObsolete = it->second.m_isObsolete; + + return it->second.m_style->clone(); + } + + void getAllTags(vector &tags) + { + tags.clear(); + tags.reserve(m_table.size()); + for (Table::iterator it = m_table.begin(); + it != m_table.end(); ++it) + if (!it->second.m_isObsolete) + tags.push_back(it->first); + } + + ~ColorStyleList() + { + Table::iterator it = m_table.begin(); + for (; it != m_table.end(); ++it) { + delete it->second.m_style; + } + } + +private: + // not implemented + ColorStyleList(const ColorStyleList &); + ColorStyleList &operator=(const ColorStyleList &); +}; + +//----------------------------------------------------------------------------- + +} // namespace + +//=================================================================== + +void TColorStyle::declare(TColorStyle *style) +{ + ColorStyleList::instance()->declare(style); +} + +//=================================================================== + +double computeAverageThickness(const TStroke *s, double &minThickness, double &maxThickness) +{ + int count = s->getControlPointCount(); + + minThickness = 1000; + maxThickness = -1; + double resThick = 0; + + for (int i = 0; i < s->getControlPointCount(); i++) { + double thick = s->getControlPoint(i).thick; + if (i >= 2 && i < s->getControlPointCount() - 2) + resThick += thick; + + if (thick < minThickness) + minThickness = thick; + if (thick > maxThickness) + maxThickness = thick; + } + + if (count < 6) + return s->getControlPoint(count / 2 + 1).thick; + return resThick / (s->getControlPointCount() - 4); +} + +void TColorStyle::drawStroke(TFlash &flash, const TStroke *s) const +{ + bool isCenterline = false; + double minThickness, maxThickness = 0; + wstring quality = flash.getLineQuality(); + double thickness = computeAverageThickness(s, minThickness, maxThickness); + if (minThickness == maxThickness && minThickness == 0) + return; + if (quality == TFlash::ConstantLines) + isCenterline = true; + else if (quality == TFlash::MixedLines && (maxThickness == 0 || minThickness / maxThickness > 0.5)) + isCenterline = true; + else if (quality == TFlash::VariableLines && maxThickness - minThickness < 0.16) // Quando si salva il pli, si approssima al thick. + // L'errore di approx e' sempre 0.1568... + isCenterline = true; + //else assert(false); + + flash.setFillColor(getAverageColor()); + //flash.setFillColor(TPixel::Red); + + TStroke *saux = const_cast(s); + if (isCenterline) { + saux->setAverageThickness(thickness); + flash.setThickness(s->getAverageThickness()); + flash.setLineColor(getAverageColor()); + flash.drawCenterline(s, false); + } else { + saux->setAverageThickness(0); + if (!flash.drawOutline(saux)) { + flash.setThickness(thickness); + flash.setLineColor(getAverageColor()); + flash.drawCenterline(s, false); + } + } +} + +//----------------------------------------------------------------------------- +// Format: _123 |global name +// _123 = flag; optional (*) +// |global = global name; optional +// name = color name; mandatory (??) + +// note: If name starts with a digit or by '_', another '_' +// is added. +// (*): In such case, the flag is mandatory. +void TColorStyle::save(TOutputStreamInterface &os) const +{ + wstring name = getName(); + bool numberedName = !name.empty() && ('0' <= name[0] && name[0] <= '9' || name[0] == '_'); + + if (m_flags > 0 || (name.length() == 1 && numberedName)) + os << ("_" + QString::number(m_flags)).toStdString(); + wstring gname = getGlobalName(); + wstring origName = getOriginalName(); + + if (gname != L"") { + os << toString(L"|" + gname); + + //save the original name from studio palette + if (origName != L"") { + //write two "@"s if the edited flag is ON + os << toString(((m_isEditedFromOriginal) ? L"@@" : L"@") + origName); + } + } + + if (numberedName) + name.insert(0, L"_"); + + os << toString(name) << (int)getTagId(); + saveData(os); +} + +//------------------------------------------------------------------- + +TColorStyle *TColorStyle::load(TInputStreamInterface &is) +{ + string name; + wstring gname; + wstring origName; + bool isEdited = false; + + is >> name; + unsigned int flags = 0; + if (name.length() == 2 && name[0] == '_' && '0' <= name[1] && name[1] <= '9') { + flags = QString::fromStdString(name.substr(1)).toUInt(); + is >> name; + } + if (name.length() > 0 && name[0] == '|') { + gname = toWideString(name.substr(1)); + is >> name; + + //If the style is copied from studio palette, original name is here + if (name.length() > 0 && name[0] == '@') { + //if there are two "@"s, then activate the edited flag + if (name[1] == '@') { + isEdited = true; + origName = toWideString(name.substr(2)); + } else { + origName = toWideString(name.substr(1)); + } + is >> name; + } + } + int id = 0; + if (!name.empty() && '0' <= name[0] && name[0] <= '9') { + id = toInt(name); + name = "color"; + } else { + if (!name.empty() && name[0] == '_') + name.erase(name.begin()); + is >> id; + } + bool isObsolete = false; + TColorStyle *style = ColorStyleList::instance()->create(id, isObsolete); + assert(style); + style->setFlags(flags); + if (isObsolete) + style->loadData(id, is); + else + style->loadData(is); + style->setName(toWideString(name)); + style->setGlobalName(gname); + style->setOriginalName(origName); + style->setIsEditedFlag(isEdited); + return style; +} + +//------------------------------------------------------------------- + +TColorStyle *TColorStyle::create(int tagId) +{ + bool isObsolete = false; + return ColorStyleList::instance()->create(tagId, isObsolete); +} + +//------------------------------------------------------------------- + +void TColorStyle::getAllTags(vector &tags) +{ + ColorStyleList::instance()->getAllTags(tags); +} diff --git a/toonz/sources/common/tvrender/tellipticbrush.cpp b/toonz/sources/common/tvrender/tellipticbrush.cpp new file mode 100644 index 0000000..472fc8b --- /dev/null +++ b/toonz/sources/common/tvrender/tellipticbrush.cpp @@ -0,0 +1,1285 @@ + + +//Toonz components includes +#include "tcurveutil.h" +#include "tinterval.h" + +#include "tellipticbrushP.h" +#include "tstrokeoutline.h" + +using namespace tellipticbrush; + +//******************************************************************************** +// EXPLANATION +//******************************************************************************** + +/*! \file tellipticbrush.cpp +This code deals with the "outlinization" process of a TStroke instance. + +The process of extracing the outline of a thick stroke can be resumed in 2 main steps: + + 1. Discretize the stroke centerline in the most appropriate centerline points, + extracting infos about position and left/right derivatives at each. + + 2. Build the outline points associated to each individual centerline point; + eventually including additional junction points and caps. + +The first major step has some sub-routines worth noting: + + 1.1 Isolate regions of the stroke where the thickness speed is greater + than the gemoetrical speed of the centerline. These points are 'self-covered' + by their immediate neighbourhood, and thus cannot be seen - or build outline directions. + 1.2 Some procedural style need to sample the centerline at a given length step. + 1.3 The centerline should be sampled so that the resulting polygonal outline + approximation is tightly close to the theoretical outline, up to an error bound. + The recursive approach is the simplest to deal with this issue. + +The second step implements different outline styles to extrude the centerline points. +*/ + +//******************************************************************************** +// Geometric Helper Functions +//******************************************************************************** + +//! Returns the distance between two points +double tellipticbrush::dist(const TPointD &P1, const TPointD &P2) +{ + return norm(P2 - P1); +} + +//------------------------------------------------------------ + +//! Returns the distance between two points +double tellipticbrush::dist(const TThickPoint &P1, const TThickPoint &P2) +{ + return norm(P2 - P1); +} + +//------------------------------------------------------------ + +//!Returns the angle between (unnormalized) vectors v1 and v2 +double tellipticbrush::angle(const TPointD &v1, const TPointD &v2) +{ + TPointD d1(v1 * (1.0 / norm(v1))), d2(v2 * (1.0 / norm(v2))); + return atan2(cross(v1, v2), v1 * v2); +} + +//------------------------------------------------------------ + +/*! + Returns the intersection between two lines in the form of \b coordinates + from a pair of the lines' starting points. Passed directions must have norm 1. + + If the system's determinant modulus is under the specified tolerance parameter, + TConsts::napd is returned. +*/ +TPointD tellipticbrush::intersectionCoords( + const TPointD &P0, const TPointD &d0, const TPointD &P1, const TPointD &d1, + double detTol) +{ + //Solve P0 + x * d0 == P1 + y * d1 + + double det = d0.y * d1.x - d0.x * d1.y; + if (fabs(det) < detTol) + return TConsts::napd; + + TPointD P1_P0(P1 - P0); + return TPointD( + (d1.x * P1_P0.y - d1.y * P1_P0.x) / det, + (d0.x * P1_P0.y - d0.y * P1_P0.x) / det); +} + +//------------------------------------------------------------ + +/*! + Returns the left or right envelope direction of centerline point p against thick direction d. +*/ +void tellipticbrush::buildEnvelopeDirection( + const TThickPoint &p, const TThickPoint &d, bool left, TPointD &res) +{ + double dNorm2 = sq(d.x) + sq(d.y); + + double a = -d.thick / dNorm2; + double b = sqrt(dNorm2 - sq(d.thick)) / dNorm2; + + TPointD n(left ? TPointD(-d.y, d.x) : TPointD(d.y, -d.x)); + res = a * TPointD(d.x, d.y) + b * n; +} + +//------------------------------------------------------------ + +void tellipticbrush::buildEnvelopeDirections( + const TThickPoint &p, const TThickPoint &d, TPointD &resL, TPointD &resR) +{ + double dNorm2 = sq(d.x) + sq(d.y); + + double a = -d.thick / dNorm2; + double b = sqrt(dNorm2 - sq(d.thick)) / dNorm2; + + TPointD n(-d.y, d.x); + resL = a * TPointD(d.x, d.y) + b * n; + resR = a * TPointD(d.x, d.y) - b * n; +} + +//------------------------------------------------------------ + +/*! + Extrudes centerline point p against thick direction d, returning its left or right + envelope displacement vector. +*/ +void tellipticbrush::buildEnvelopeVector( + const TThickPoint &p, const TThickPoint &d, bool left, TPointD &res) +{ + buildEnvelopeDirection(p, d, left, res); + res.x = p.thick * res.x; + res.y = p.thick * res.y; +} + +//------------------------------------------------------------ + +void tellipticbrush::buildEnvelopeVectors( + const TThickPoint &p, const TThickPoint &d, TPointD &resL, TPointD &resR) +{ + buildEnvelopeDirections(p, d, resL, resR); + resL.x = p.thick * resL.x; + resL.y = p.thick * resL.y; + resR.x = p.thick * resR.x; + resR.y = p.thick * resR.y; +} + +//------------------------------------------------------------ + +/*! + Builds the angle that supports a *quality* discretization of the circle + with maximal error < m_pixSize. +*/ +void tellipticbrush::buildAngularSubdivision( + double radius, double angle, double err, int &nAngles) +{ + /* + See "Graphic Gems", page 600. + + NOTE: maxAngle is not multiplied by 2.0 as the naive pythagorical + argument would pretend. The 2.0 holds if we want to find the angle + at which the distance of the circle from its approximation is always < error. + + But we want MORE. We want that to happen against the distance from EVERY + TANGENT LINE of the arc - not the arc itself. + This is coherent with the assumption that pixels orientation is not known. + + It's easy to see that maxAngle just has to be not multiplied by 2. + */ + + double maxAngle = acos(1.0 - err / radius); //* 2.0; + nAngles = tceil(fabs(angle) / maxAngle); +} + +//------------------------------------------------------------ + +TRectD tellipticbrush::computeBBox(const TStroke &stroke) +{ + TRectD bbox; + + int i, n = stroke.getChunkCount(); + for (i = 0; i < n; i++) + bbox += stroke.getChunk(i)->getBBox(); + + return bbox; +} + +//******************************************************************************** +// CenterlinePoint implementation +//******************************************************************************** + +void tellipticbrush::CenterlinePoint::buildPos(const TStroke &stroke) +{ + if (m_posBuilt) + return; + + m_p = stroke.getChunk(m_chunkIdx)->getThickPoint(m_t); + m_posBuilt = true; +} + +//------------------------------------------------------------ + +void tellipticbrush::CenterlinePoint::buildDirs(const TStroke &stroke) +{ + if (m_dirsBuilt) + return; + + int chunkPrev, chunkNext; + double tPrev, tNext; + bool coveredPrev, coveredNext; + + //Discriminate the boundary cases + bool quadBoundary; + if (m_t == 0.0) { + quadBoundary = true; + chunkPrev = m_chunkIdx - 1, chunkNext = m_chunkIdx; + tPrev = 1.0, tNext = 0.0; + } else if (m_t == 1.0) { + quadBoundary = true; + chunkPrev = m_chunkIdx, chunkNext = m_chunkIdx + 1; + tPrev = 1.0, tNext = 0.0; + } else { + quadBoundary = false; + chunkPrev = chunkNext = m_chunkIdx; + tPrev = tNext = m_t; + } + + //Build the backward direction + if (chunkPrev >= 0) { + const TThickQuadratic *ttqPrev = stroke.getChunk(chunkPrev); + + const TThickPoint &P0 = ttqPrev->getThickP0(); + const TThickPoint &P1 = ttqPrev->getThickP1(); + const TThickPoint &P2 = ttqPrev->getThickP2(); + + if (quadBoundary && (P1 == P2)) + m_prevD = P2 - P0; //Toonz 'Linear' CPs. Eliminating a perilous singularity this way. + else { + m_prevD.x = 2.0 * ((P1.x - P0.x) + tPrev * (P0.x - 2.0 * P1.x + P2.x)); + m_prevD.y = 2.0 * ((P1.y - P0.y) + tPrev * (P0.y - 2.0 * P1.y + P2.y)); + m_prevD.thick = 2.0 * ((P1.thick - P0.thick) + tPrev * (P0.thick - 2.0 * P1.thick + P2.thick)); + } + + //Points whose thickness derivative does exceeds the point speed + //cannot project envelope directions for that direction. This needs to be known. + coveredPrev = (sq(m_prevD.x) + sq(m_prevD.y) < sq(m_prevD.thick) + tolPar); + + //Accept only uncovered derivatives + m_hasPrevD = !coveredPrev; + } else { + m_hasPrevD = false; + coveredPrev = true; //ie prev coverage must not affect next coverage + m_prevD = TConsts::natp; + } + + //Build the forward direction + if (chunkPrev == chunkNext) { + //If the quadratic is the same, no need to derive it twice + m_hasNextD = m_hasPrevD; + m_nextD = m_prevD; + coveredNext = coveredPrev; + } else if (chunkNext < stroke.getChunkCount()) { + const TThickQuadratic *ttqNext = stroke.getChunk(chunkNext); + + const TThickPoint &P0 = ttqNext->getThickP0(); + const TThickPoint &P1 = ttqNext->getThickP1(); + const TThickPoint &P2 = ttqNext->getThickP2(); + + if (quadBoundary && (P0 == P1)) + m_nextD = P2 - P0; + else { + m_nextD.x = 2.0 * ((P1.x - P0.x) + tNext * (P0.x - 2.0 * P1.x + P2.x)); + m_nextD.y = 2.0 * ((P1.y - P0.y) + tNext * (P0.y - 2.0 * P1.y + P2.y)); + m_nextD.thick = 2.0 * ((P1.thick - P0.thick) + tNext * (P0.thick - 2.0 * P1.thick + P2.thick)); + } + + coveredNext = (sq(m_nextD.x) + sq(m_nextD.y) < sq(m_nextD.thick) + tolPar); + m_hasNextD = !coveredNext; + } else { + m_hasNextD = false; + coveredNext = true; //ie prev coverage must not affect next coverage + m_nextD = TConsts::natp; + } + + m_covered = (coveredPrev && coveredNext); + + m_dirsBuilt = true; +} + +//******************************************************************************** +// Specialized Linearizator for common stroke drawing +//******************************************************************************** + +namespace +{ + +class LengthLinearizator : public tellipticbrush::StrokeLinearizator +{ + double m_lengthStep; + int m_countIdx; + +public: + LengthLinearizator(const TStroke *stroke, double lengthStep) + : StrokeLinearizator(stroke), m_lengthStep(lengthStep), m_countIdx(0) {} + + void linearize(std::vector &cPoints, int chunk); +}; + +//-------------------------------------------------------------------------------------------- + +void LengthLinearizator::linearize(std::vector &cPoints, int chunk) +{ + if (m_lengthStep == 0.0) + return; + + //Retrieve the stroke length at stroke start + double startW = this->m_stroke->getW(chunk, 0.0); + double startLength = this->m_stroke->getLength(startW); + + //Retrieve the quadratic's end length + const TThickQuadratic *ttq = this->m_stroke->getChunk(chunk); + double endLength = startLength + ttq->getLength(); + + //Build the step-length inside the chunk + int n = tceil(startLength / m_lengthStep); + double length; + double t, w; + int chk; + + for (length = n * m_lengthStep; length < endLength; length += m_lengthStep) { + //Retrieve the new params at length. Need to use the sloppy TStroke interface, + //unfortunately... + w = this->m_stroke->getParameterAtLength(length); + + //WARNING: TStroke's interface is COMPLETELY WRONG about what gets returned + //by the following function. This is just *CRAZY* - however, let's take it all right... + bool ok = !this->m_stroke->getChunkAndT(w, chk, t); + + //In case something goes wrong, skip + if (!ok || chk != chunk) + continue; + + //Store the param, that NEEDS TO BE INCREMENTALLY COUNTED - as length linearization + //is typically used for special procedural vector styles that need this info. + CenterlinePoint cPoint(chk, t); + cPoint.m_countIdx = m_countIdx += 2; //++m_countIdx; + cPoints.push_back(cPoint); + } +} + +//============================================================================================ + +class RecursiveLinearizator : public tellipticbrush::StrokeLinearizator +{ + double m_pixSize; + +public: + RecursiveLinearizator(const TStroke *stroke, double pixSize) + : StrokeLinearizator(stroke), m_pixSize(pixSize) {} + + void linearize(std::vector &cPoints, int chunk); + void subdivide(std::vector &cPoints, + CenterlinePoint &cp0, CenterlinePoint &cp1); +}; + +//-------------------------------------------------------------------------------------------- + +void RecursiveLinearizator::linearize(std::vector &cPoints, int chunk) +{ + /* + Recursively linearizes the centerline, in the following way: + + Take one point, together with the next. Add a point in the middle interval, until + the next thick point is included (up to pixSize) in the 'forward-cast' envelope of + current one. If the midpoint was added, repeat on the 2 sub-intervals. + */ + + const TStroke &stroke = *this->m_stroke; + const TThickQuadratic &ttq = *stroke.getChunk(chunk); + + //Sort the interval (SHOULD BE DONE OUTSIDE?) + std::sort(cPoints.begin(), cPoints.end()); + + std::vector addedPoints; + + unsigned int i, size_1 = cPoints.size() - 1; + for (i = 0; i < size_1; ++i) { + cPoints[i].buildPos(stroke); + cPoints[i].buildDirs(stroke); + + cPoints[i + 1].buildPos(stroke); + cPoints[i + 1].buildDirs(stroke); + + subdivide(addedPoints, cPoints[i], cPoints[i + 1]); + } + + cPoints[size_1].buildPos(stroke); + cPoints[size_1].buildDirs(stroke); + + CenterlinePoint cpEnd(chunk, 1.0); + { + const TThickPoint &P1(ttq.getThickP1()); + cpEnd.m_p = ttq.getThickP2(); + cpEnd.m_prevD = TThickPoint( + 2.0 * (cpEnd.m_p.x - P1.x), + 2.0 * (cpEnd.m_p.y - P1.y), + 2.0 * (cpEnd.m_p.thick - P1.thick)); + cpEnd.m_hasPrevD = true; //The effective false case should already be dealt by sqrt... + } + + subdivide(addedPoints, cPoints[size_1], cpEnd); + + cPoints.insert(cPoints.end(), addedPoints.begin(), addedPoints.end()); +} + +//-------------------------------------------------------------------------------------------- + +void RecursiveLinearizator::subdivide(std::vector &cPoints, + CenterlinePoint &cp0, CenterlinePoint &cp1) +{ + if (!(cp0.m_hasNextD && cp1.m_hasPrevD)) + return; + + //Build the distance of next from the outline of cp's 'envelope extension' + + TPointD envDirL0, envDirR0, envDirL1, envDirR1; + buildEnvelopeDirections(cp0.m_p, cp0.m_nextD, envDirL0, envDirR0); + buildEnvelopeDirections(cp1.m_p, cp1.m_prevD, envDirL1, envDirR1); + + TPointD diff(convert(cp1.m_p) - convert(cp0.m_p)); + double d = tmax( + fabs(envDirL0 * (diff + cp1.m_p.thick * envDirL1 - cp0.m_p.thick * envDirL0)), + fabs(envDirR0 * (diff + cp1.m_p.thick * envDirR1 - cp0.m_p.thick * envDirR0))); + + if (d > m_pixSize && + cp1.m_t - cp0.m_t > 1e-4) { + double midT = 0.5 * (cp0.m_t + cp1.m_t); + CenterlinePoint midPoint(cp0.m_chunkIdx, midT); + + midPoint.buildPos(*this->m_stroke); + midPoint.buildDirs(*this->m_stroke); + + subdivide(cPoints, cp0, midPoint); + subdivide(cPoints, midPoint, cp1); + + cPoints.push_back(midPoint); + } +} + +//============================================================================================ + +class CoverageLinearizator : public tellipticbrush::StrokeLinearizator +{ +public: + CoverageLinearizator(const TStroke *stroke) + : StrokeLinearizator(stroke) {} + + void linearize(std::vector &cPoints, int chunk); +}; + +//-------------------------------------------------------------------------------------------- + +void CoverageLinearizator::linearize(std::vector &cPoints, int chunk) +{ + //Retrieve the at max 2 parameters for which: + // sq(d.x) + sq(d.y) == sq(d.thick) + tolPar(*) (ie, "self-coverage" critical points) + + //It can be rewritten in the canonical form: at^2 + bt + c == 0 + + const TThickQuadratic &ttq(*this->m_stroke->getChunk(chunk)); + + TThickPoint P0(ttq.getThickP0()), P1(ttq.getThickP1()), P2(ttq.getThickP2()); + if ((P0 == P1) || (P1 == P2)) + return; //Linear speed out/in case. Straighted up in the buildDirs() + + //Remember that d = 2 [P1 - P0 + t (P0 + P2 - 2 P1)] + + T3DPointD u(P1.x - P0.x, P1.y - P0.y, P1.thick - P0.thick); + T3DPointD v(P0.x + P2.x - 2.0 * P1.x, P0.y + P2.y - 2.0 * P1.y, P0.thick + P2.thick - 2.0 * P1.thick); + + double a = sq(v.x) + sq(v.y) - sq(v.z); + if (fabs(a) < 1e-4) + return; //Little (acceleration) quadratics case + + //(*) Build tolerance - 2.0 since tolPar is already used to discriminate 'good' dirs. Ours must be. + const double twiceTolPar = 2.0 * tolPar; + + double b = 2.0 * (u.x * v.x + u.y * v.y - u.z * v.z); + double c = sq(u.x) + sq(u.y) - sq(u.z) - twiceTolPar; + + double delta = sq(b) - 4.0 * a * c; + if (delta < 0) + return; + + double sqrtDelta = sqrt(delta); + double t0 = (-b - sqrtDelta) / (2.0 * a); + double t1 = (-b + sqrtDelta) / (2.0 * a); + + if (t0 > 0 && t0 < 1) { + CenterlinePoint cp(chunk, t0); + cp.buildPos(*this->m_stroke); + cp.buildDirs(*this->m_stroke); + cp.m_hasNextD = false; + cPoints.push_back(cp); + } + + if (t1 > 0 && t1 < 1) { + CenterlinePoint cp(chunk, t1); + cp.buildPos(*this->m_stroke); + cp.buildDirs(*this->m_stroke); + cp.m_hasPrevD = false; + cPoints.push_back(cp); + } +} + +} //namespace + +//******************************************************************************** +// Outline Builder implementation +//******************************************************************************** + +tellipticbrush::OutlineBuilder::OutlineBuilder(const OutlinizationData &data, const TStroke &stroke) + : m_pixSize(data.m_pixSize), m_oOptions(stroke.outlineOptions()), m_lastChunk(stroke.getChunkCount() - 1) +{ + typedef TStroke::OutlineOptions OutlineOptions; + + switch (m_oOptions.m_capStyle) { + case OutlineOptions::PROJECTING_CAP: { + m_addBeginCap = &OutlineBuilder::addProjectingBeginCap>; + m_addEndCap = &OutlineBuilder::addProjectingEndCap>; + m_addBeginCap_ext = &OutlineBuilder::addProjectingBeginCap; + m_addEndCap_ext = &OutlineBuilder::addProjectingEndCap; + break; + } + case OutlineOptions::BUTT_CAP: { + m_addBeginCap = &OutlineBuilder::addButtBeginCap; + m_addEndCap = &OutlineBuilder::addButtEndCap; + m_addBeginCap_ext = 0; + m_addEndCap_ext = 0; + break; + } + case OutlineOptions::ROUND_CAP: + default: + m_addBeginCap = &OutlineBuilder::addRoundBeginCap; + m_addEndCap = &OutlineBuilder::addRoundEndCap; + m_addBeginCap_ext = 0; + m_addEndCap_ext = 0; + }; + + switch (m_oOptions.m_joinStyle) { + case OutlineOptions::MITER_JOIN: { + m_addSideCaps = &OutlineBuilder::addMiterSideCaps>; + m_addSideCaps_ext = &OutlineBuilder::addMiterSideCaps; + break; + } + + case OutlineOptions::BEVEL_JOIN: { + m_addSideCaps = &OutlineBuilder::addBevelSideCaps; + m_addSideCaps_ext = 0; + break; + } + case OutlineOptions::ROUND_JOIN: + default: + m_addSideCaps = &OutlineBuilder::addRoundSideCaps; + m_addSideCaps_ext = 0; + }; +} + +//------------------------------------------------------------ + +/*! + Translates a CenterlinePoint instance into OutlinePoints, and + adds them to the supplied vector container. +*/ +void tellipticbrush::OutlineBuilder::buildOutlinePoints( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //If the centerline directions exist and match, just add their envelope + //displacement directly + if (cPoint.m_hasPrevD && cPoint.m_hasNextD && + cPoint.m_prevD == cPoint.m_nextD) { + TPointD leftD, rightD; + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftD); + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightD); + + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + rightD, cPoint.m_countIdx)); + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + leftD, cPoint.m_countIdx)); + } else { + //We have to add caps/joins together with the envelope displacements + //Caps which are not at stroke ends are always imposed to be round. + + if (cPoint.m_hasPrevD) { + if (cPoint.m_hasNextD) + (this->*m_addSideCaps)(oPoints, cPoint); + else if (cPoint.m_chunkIdx == m_lastChunk && cPoint.m_t == 1.0) + (this->*m_addEndCap)(oPoints, cPoint); + else + addRoundEndCap(oPoints, cPoint); + } else { + if (cPoint.m_hasNextD) + if (cPoint.m_chunkIdx == 0 && cPoint.m_t == 0.0) + (this->*m_addBeginCap)(oPoints, cPoint); + else + addRoundBeginCap(oPoints, cPoint); + else + addCircle(oPoints, cPoint); + } + } +} + +//------------------------------------------------------------ + +/*! + Translates a CenterlinePoint instance into bounding box points, + and adds them to the supplied (bbox) rect. +*/ +void tellipticbrush::OutlineBuilder::buildOutlineExtensions( + TRectD &bbox, const CenterlinePoint &cPoint) +{ + if (!(cPoint.m_hasPrevD && cPoint.m_hasNextD && + cPoint.m_prevD == cPoint.m_nextD)) { + //Only non-envelope points are interesting to the bbox builder procedure + + if (cPoint.m_hasPrevD) { + if (cPoint.m_hasNextD && + m_addSideCaps_ext) + (this->*m_addSideCaps_ext)(bbox, cPoint); + else if (cPoint.m_chunkIdx == m_lastChunk && cPoint.m_t == 1.0 && + m_addEndCap_ext) + (this->*m_addEndCap_ext)(bbox, cPoint); + } else { + if (cPoint.m_hasNextD) + if (cPoint.m_chunkIdx == 0 && cPoint.m_t == 0.0 && + m_addBeginCap_ext) + (this->*m_addBeginCap_ext)(bbox, cPoint); + } + } +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addCircularArcPoints( + int idx, std::vector &outPoints, + const TPointD ¢er, const TPointD &ray, double angle, int nAngles, + int countIdx) +{ + TPointD rotRay(ray); + + //Push the initial point without rotation + outPoints[idx] = TOutlinePoint(center + ray, countIdx); + idx += 2; + + //Build the rotation + double sin_a = sin(angle); //NOTE: The 'angle' input parameter CANNOT be substituted with just cos, + double cos_a = cos(angle); //while sin = sqrt(1.0 - sq(cos)), BECAUSE this way sin is ALWAYS > 0 + + int i; + for (i = 1; i <= nAngles; ++i, idx += 2) { + rotRay = TPointD( + rotRay.x * cos_a - rotRay.y * sin_a, + rotRay.x * sin_a + rotRay.y * cos_a); + outPoints[idx] = center + rotRay; + } +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addCircle( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //Build the angle step for (0, pi) + int nAngles; + double stepAngle, totAngle = angle(TPointD(1.0, 0.0), TPointD(-1.0, 0.0)); + + buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles); + stepAngle = totAngle / (double)nAngles; + + //Resize the vector to store the required points + int idx = oPoints.size(); + oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD())); + + //Add the circle points from each semi-circle + addCircularArcPoints(idx, oPoints, + convert(cPoint.m_p), TPointD(cPoint.m_p.thick, 0.0), + -stepAngle, nAngles, cPoint.m_countIdx); + addCircularArcPoints(idx + 1, oPoints, + convert(cPoint.m_p), TPointD(cPoint.m_p.thick, 0.0), + stepAngle, nAngles, cPoint.m_countIdx); +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addRoundBeginCap( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + TPointD rightD; + buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, false, rightD); + + TPointD beginD(-convert(cPoint.m_nextD)); + beginD = (cPoint.m_p.thick / norm(beginD)) * beginD; + + int nAngles; + double stepAngle, totAngle = angle(beginD, rightD); + + buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles); + stepAngle = totAngle / (double)nAngles; + + int idx = oPoints.size(); + oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD())); + + addCircularArcPoints(idx, oPoints, + convert(cPoint.m_p), beginD, + stepAngle, nAngles, cPoint.m_countIdx); + addCircularArcPoints(idx + 1, oPoints, + convert(cPoint.m_p), beginD, + -stepAngle, nAngles, cPoint.m_countIdx); //we just need to take the opposite angle to deal with left side +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addRoundEndCap( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //Build the backward envelope directions + //Note that the situation is specular on the left and right side... + TPointD leftD, rightD; + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftD); + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightD); + + int nAngles; + double stepAngle, totAngle = angle(rightD, convert(cPoint.m_prevD)); + + buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles); + stepAngle = totAngle / (double)nAngles; + + int idx = oPoints.size(); + oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD())); + + addCircularArcPoints(idx, oPoints, + convert(cPoint.m_p), rightD, + stepAngle, nAngles, cPoint.m_countIdx); + addCircularArcPoints(idx + 1, oPoints, + convert(cPoint.m_p), leftD, + -stepAngle, nAngles, cPoint.m_countIdx); //we just need to take the opposite angle to deal with left side +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addButtBeginCap( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //Just add the 2 basic envelope points + TPointD leftDNext, rightDNext; + buildEnvelopeVectors(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext); + + //PLUS, add their midpoint, since it generates this part of stroke antialias... + TPointD leftP(convert(cPoint.m_p) + leftDNext), rightP(convert(cPoint.m_p) + rightDNext); + TPointD midP(0.5 * (leftP + rightP)); + + oPoints.push_back(midP); + oPoints.push_back(midP); + + oPoints.push_back(TOutlinePoint(rightP, cPoint.m_countIdx)); + oPoints.push_back(TOutlinePoint(leftP, cPoint.m_countIdx)); +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addButtEndCap( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + TPointD leftDPrev, rightDPrev; + buildEnvelopeVectors(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev); + + TPointD leftP(convert(cPoint.m_p) + leftDPrev), rightP(convert(cPoint.m_p) + rightDPrev); + TPointD midP(0.5 * (leftP + rightP)); + + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + rightDPrev, cPoint.m_countIdx)); + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + leftDPrev, cPoint.m_countIdx)); + + oPoints.push_back(midP); + oPoints.push_back(midP); +} + +//------------------------------------------------------------ + +template +void tellipticbrush::OutlineBuilder::addProjectingBeginCap( + T &oPoints, const CenterlinePoint &cPoint) +{ + double thick = cPoint.m_p.thick; + + //Find the base points + TPointD leftDNext, rightDNext; + buildEnvelopeDirections(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext); + + TPointD leftP(convert(cPoint.m_p) + thick * leftDNext); + TPointD rightP(convert(cPoint.m_p) + thick * rightDNext); + + //Add the intersections between the envelope directions' orthogonals and the + //direction orthogonals + TPointD dir(normalize(-cPoint.m_nextD)); + TPointD dirP(convert(cPoint.m_p) + thick * dir); + + TPointD cornerLCoords = intersectionCoords( + dirP, TPointD(dir.y, -dir.x), leftP, TPointD(-leftDNext.y, leftDNext.x)); + + TPointD cornerRCoords = intersectionCoords( + dirP, TPointD(-dir.y, dir.x), rightP, TPointD(rightDNext.y, -rightDNext.x)); + + if (cornerLCoords.x < 0 || cornerRCoords.y < 0) + return; + + //As before, midPoints must be added due to antialias + TPointD cornerL(dirP + cornerLCoords.x * TPointD(dir.y, -dir.x)); + TPointD cornerR(dirP + cornerRCoords.x * TPointD(-dir.y, dir.x)); + TPointD midP(0.5 * (cornerL + cornerR)); + + addEnvelopePoint(oPoints, midP); + addEnvelopePoint(oPoints, midP); + + addExtensionPoint(oPoints, cornerR); + addExtensionPoint(oPoints, cornerL); + + //Initial points must be added later, in the begin case + addEnvelopePoint(oPoints, rightP, cPoint.m_countIdx); + addEnvelopePoint(oPoints, leftP, cPoint.m_countIdx); +} + +//------------------------------------------------------------ + +template +void tellipticbrush::OutlineBuilder::addProjectingEndCap( + T &oPoints, const CenterlinePoint &cPoint) +{ + double thick = cPoint.m_p.thick; + + //Add the base points + TPointD leftDPrev, rightDPrev; + buildEnvelopeDirections(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev); + + TPointD leftP(convert(cPoint.m_p) + thick * leftDPrev); + TPointD rightP(convert(cPoint.m_p) + thick * rightDPrev); + + addEnvelopePoint(oPoints, rightP, cPoint.m_countIdx); + addEnvelopePoint(oPoints, leftP, cPoint.m_countIdx); + + //Add the intersections between the envelope directions' orthogonals and the + //direction orthogonals + TPointD dir(normalize(cPoint.m_prevD)); + TPointD dirP(convert(cPoint.m_p) + thick * dir); + + TPointD cornerLCoords = intersectionCoords( + dirP, TPointD(-dir.y, dir.x), leftP, TPointD(leftDPrev.y, -leftDPrev.x)); + + TPointD cornerRCoords = intersectionCoords( + dirP, TPointD(dir.y, -dir.x), rightP, TPointD(-rightDPrev.y, rightDPrev.x)); + + if (cornerLCoords.x < 0 || cornerRCoords.y < 0) + return; + + TPointD cornerL(dirP + cornerLCoords.x * TPointD(-dir.y, dir.x)); + TPointD cornerR(dirP + cornerRCoords.x * TPointD(dir.y, -dir.x)); + TPointD midP(0.5 * (cornerL + cornerR)); + + addExtensionPoint(oPoints, cornerR); + addExtensionPoint(oPoints, cornerL); + + addEnvelopePoint(oPoints, midP); + addEnvelopePoint(oPoints, midP); +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addRoundSideCaps( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //Side caps - this has only sense when the backward and forward direction-derivatives + //are different. This means that thay build different envelope directions. So, we add + //side caps to cover the 'elbow fractures' + + TPointD leftDPrev, leftDNext, rightDPrev, rightDNext; + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftDPrev); + buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, true, leftDNext); + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightDPrev); + buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, false, rightDNext); + + //This time, angle step is NOT specular + int nAnglesL, nAnglesR; + double totAngleL = angle(leftDPrev, leftDNext); + double totAngleR = angle(rightDPrev, rightDNext); + + //The common case is that these angles have the same sign - thus building + //opposites arcs of a circle + if (tsign(totAngleL) != tsign(totAngleR)) { + //However, there may be exceptions. We must still impose + //the constraint about 'covering opposite arcs of a circle' - + //it is necessary to make the outline look consistently filled. + + TPointD prevD(convert(cPoint.m_prevD)), nextD(convert(cPoint.m_nextD)); + + //The only dangerous case is when the directions are near-opposed + if (prevD * nextD < 0) { + const double twice_pi = 2 * TConsts::pi; + + //Here, we must make one angle its (sign-opposite) 2*pi complement. + //Keep the angle with the least fabs (smallest 'butterfly intersection') + if (fabs(totAngleL) < fabs(totAngleR)) + totAngleR = (totAngleR > 0) ? totAngleR - twice_pi : totAngleR + twice_pi; + else + totAngleL = (totAngleL > 0) ? totAngleL - twice_pi : totAngleL + twice_pi; + } + } + + buildAngularSubdivision(cPoint.m_p.thick, totAngleL, m_pixSize, nAnglesL); + buildAngularSubdivision(cPoint.m_p.thick, totAngleR, m_pixSize, nAnglesR); + + int nAngles = tmax(nAnglesL, nAnglesR); + double stepAngleL = totAngleL / (double)nAngles; + double stepAngleR = totAngleR / (double)nAngles; + + if (nAnglesL == 1 && nAnglesR == 1 && + fabs(totAngleL) < 0.525 && fabs(totAngleR) < 0.525) //angle < 30 degrees + { + //Simple case + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + rightDPrev, cPoint.m_countIdx)); + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + leftDPrev, cPoint.m_countIdx)); + } else { + int idx = oPoints.size(); + oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD())); + + addCircularArcPoints(idx, oPoints, + convert(cPoint.m_p), rightDPrev, + stepAngleR, nAngles, cPoint.m_countIdx); + addCircularArcPoints(idx + 1, oPoints, + convert(cPoint.m_p), leftDPrev, + stepAngleL, nAngles, cPoint.m_countIdx); //same angle here, as this is just a stroke direction rotation + } +} + +//------------------------------------------------------------ + +void tellipticbrush::OutlineBuilder::addBevelSideCaps( + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + //Build the envelope directions + TPointD leftDPrev, leftDNext, rightDPrev, rightDNext; + buildEnvelopeDirections(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev); + buildEnvelopeDirections(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext); + + //Add at least 2 outline points (the prevs) + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + cPoint.m_p.thick * rightDPrev, cPoint.m_countIdx)); + oPoints.push_back(TOutlinePoint(convert(cPoint.m_p) + cPoint.m_p.thick * leftDPrev, cPoint.m_countIdx)); + + //Only add the additional points when at least one of the envelope differences + //passing from prev to next is above the pixel size + if (2.0 * cPoint.m_p.thick < m_pixSize) + return; + + double threshold = sq(m_pixSize / cPoint.m_p.thick); + + double bevelSizeL = norm2(leftDNext - leftDPrev); + double bevelSizeR = norm2(rightDNext - rightDPrev); + + if (bevelSizeL > threshold || bevelSizeR > threshold) { + oPoints.push_back(convert(cPoint.m_p) + cPoint.m_p.thick * rightDNext); + oPoints.push_back(convert(cPoint.m_p) + cPoint.m_p.thick * leftDNext); + } +} + +//------------------------------------------------------------ + +template +void tellipticbrush::OutlineBuilder::addMiterSideCaps( + T &oPoints, const CenterlinePoint &cPoint) +{ + //Build the elbow side + + TPointD prevD(cPoint.m_prevD); + prevD = (1.0 / norm(prevD)) * prevD; + TPointD nextD(cPoint.m_nextD); + nextD = (1.0 / norm(nextD)) * nextD; + + double cross = prevD.x * nextD.y - prevD.y * nextD.x; + bool leftSide = (cross < 0); //ie elbow on the left side when turning right + + //Add the intersection point of the outline's tangential extensions + //'Tangential extensions' are just the orthogonals to envelope directions + + //Build envelope directions + TPointD envPrevSide, envNextSide; + buildEnvelopeDirection(cPoint.m_p, cPoint.m_prevD, leftSide, envPrevSide); + buildEnvelopeDirection(cPoint.m_p, cPoint.m_nextD, leftSide, envNextSide); + + //Build tangential directions + TPointD prevTangentialD, nextTangentialD; + if (leftSide) { + prevTangentialD = TPointD(envPrevSide.y, -envPrevSide.x); + nextTangentialD = TPointD(-envNextSide.y, envNextSide.x); + } else { + prevTangentialD = TPointD(-envPrevSide.y, envPrevSide.x); + nextTangentialD = TPointD(envNextSide.y, -envNextSide.x); + } + + //Build the outline points in the envelope directions + envPrevSide = cPoint.m_p.thick * envPrevSide; + envNextSide = cPoint.m_p.thick * envNextSide; + + TPointD p0(convert(cPoint.m_p) + envPrevSide); + TPointD p1(convert(cPoint.m_p) + envNextSide); + + //Set coordinates bounds + double lowerBound = tmax(cPoint.m_p.thick * m_oOptions.m_miterLower, m_pixSize); + double upperBound = cPoint.m_p.thick * m_oOptions.m_miterUpper; + + //Build the intersection between the 2 lines + TPointD cornerCoords(intersectionCoords(p0, prevTangentialD, p1, nextTangentialD)); + if (cornerCoords == TConsts::napd || + cornerCoords.x < lowerBound || cornerCoords.y > upperBound || + cornerCoords.y < lowerBound || cornerCoords.y > upperBound) { + //Bevel caps + addOutlineBuilderFunc(&OutlineBuilder::addBevelSideCaps, oPoints, cPoint); + return; + } + + TPointD corner(p0 + cornerCoords.x * prevTangentialD); + + TPointD envPrevNotSide, envNextNotSide; + buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, !leftSide, envPrevNotSide); + buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, !leftSide, envNextNotSide); + + TPointD notSidePrev(convert(cPoint.m_p) + envPrevNotSide); + TPointD notSideNext(convert(cPoint.m_p) + envNextNotSide); + if (leftSide) { + addEnvelopePoint(oPoints, notSidePrev, cPoint.m_countIdx); + addEnvelopePoint(oPoints, convert(cPoint.m_p) + envPrevSide, cPoint.m_countIdx); + + addExtensionPoint(oPoints, 0.5 * (notSidePrev + notSideNext)); + addExtensionPoint(oPoints, corner); + + addEnvelopePoint(oPoints, notSideNext); + addEnvelopePoint(oPoints, convert(cPoint.m_p) + envNextSide); + } else { + addEnvelopePoint(oPoints, convert(cPoint.m_p) + envPrevSide, cPoint.m_countIdx); + addEnvelopePoint(oPoints, notSidePrev, cPoint.m_countIdx); + + addExtensionPoint(oPoints, corner); + addExtensionPoint(oPoints, 0.5 * (notSidePrev + notSideNext)); + + addEnvelopePoint(oPoints, convert(cPoint.m_p) + envNextSide); + addEnvelopePoint(oPoints, notSideNext); + } +} + +//******************************************************************************** +// Outline Builder Function +//******************************************************************************** + +namespace +{ + +int buildCPointsData(const TStroke &stroke, std::vector &cPoints) +{ + //Build point positions + unsigned int i, pointsCount = cPoints.size(); + int validPointsCount = 0; + for (i = 0; i < pointsCount; ++i) { + CenterlinePoint &cPoint = cPoints[i]; + + cPoint.buildPos(stroke); + cPoint.buildDirs(stroke); + + if (!cPoint.m_covered) + //Covered points simply cannot build envelope directions (forward nor backward). + //So, don't consider them + ++validPointsCount; + } + + if (!validPointsCount) { + //Only single points may end up here. We just solve the problem + //uncovering the first point. + cPoints[0].m_covered = false; + validPointsCount = 1; + } + + return validPointsCount; +} + +} //namespace + +//------------------------------------------------------------ + +void tellipticbrush::buildOutline( + const TStroke &stroke, std::vector &cPoints, + TStrokeOutline &outline, const OutlinizationData &data) +{ + //Build the centerline points associated with passed stroke parameters + int outlineSize = buildCPointsData(stroke, cPoints); + + //Reserve the lower bound known to the outline points + std::vector &oPoints = outline.getArray(); + oPoints.reserve(2 * outlineSize); + + OutlineBuilder outBuilder(data, stroke); + + //Now, build the outline + unsigned int i, cPointsCount = cPoints.size(); + for (i = 0;; ++i) { + //Search the next uncovered point + for (; i < cPointsCount && cPoints[i].m_covered; ++i) + ; + + if (i >= cPointsCount) + break; + + //Build the associated outline points + outBuilder.buildOutlinePoints(oPoints, cPoints[i]); + } +} + +//******************************************************************************** +// Make Outline Implementation +//******************************************************************************** + +namespace +{ + +/* + Quick container to store all the linearization features to be supported. + \note The set should be appropriately ordered so that linearizator dependance + can be supported (linearizators may work depending on knowledge of the other + linearized points) +*/ +struct LinearizatorsSet { + static const int nLinearizators = 3; + + LengthLinearizator m_lengthLinearizator; + CoverageLinearizator m_coverageLinearizator; + RecursiveLinearizator m_recursiveLinearizator; + + StrokeLinearizator *m_linearizatorPtrs[nLinearizators]; + +public: + LinearizatorsSet(const TStroke &stroke, const OutlinizationData &data) + : m_lengthLinearizator(&stroke, data.m_options.m_lengthStep), m_coverageLinearizator(&stroke), m_recursiveLinearizator(&stroke, data.m_pixSize) + { + m_linearizatorPtrs[0] = &m_lengthLinearizator; + m_linearizatorPtrs[1] = &m_coverageLinearizator; + m_linearizatorPtrs[2] = &m_recursiveLinearizator; + } + + StrokeLinearizator *operator[](int i) { return m_linearizatorPtrs[i]; } + const int size() const { return nLinearizators; } +}; + +} //namespace + +//============================================================================================ + +void TOutlineUtil::makeOutline(const TStroke &stroke, + TStrokeOutline &outline, + const TOutlineUtil::OutlineParameter &options) +{ + //Build outlinization data + OutlinizationData data(options); + + //Build a set of linearizators for the specified stroke + LinearizatorsSet linearizators(stroke, data); + + std::vector cPoints, chunkPoints; + int i, chunksCount = stroke.getChunkCount(); + for (i = 0; i < chunksCount; ++i) { + chunkPoints.clear(); + chunkPoints.push_back(CenterlinePoint(i, 0.0)); + + int j, linearsCount = linearizators.size(); + for (j = 0; j < linearsCount; ++j) { + StrokeLinearizator *linearizator = linearizators[j]; + linearizator->linearize(chunkPoints, i); + } + + //These points are just PUSH_BACK'd to the vector. A sorting must be performed + //before storing them in the overall centerline points vector + std::sort(chunkPoints.begin(), chunkPoints.end()); + + cPoints.insert(cPoints.end(), chunkPoints.begin(), chunkPoints.end()); + } + + //Build the final point. + CenterlinePoint last(chunksCount - 1, 1.0); + + //In the selfLoop case, use its info to modify the initial point. + if (stroke.isSelfLoop()) { + CenterlinePoint &first = cPoints[0]; + + first.buildPos(stroke); + first.buildDirs(stroke); + last.buildPos(stroke); + last.buildDirs(stroke); + + first.m_prevD = last.m_prevD; + first.m_hasPrevD = last.m_hasPrevD; + last.m_nextD = first.m_nextD; + last.m_hasNextD = first.m_hasNextD; + first.m_covered = last.m_covered = (first.m_covered && last.m_covered); + } + + cPoints.push_back(last); + + //Now, build the outline associated to the linearized centerline + + //NOTE: It's NOT NECESSARY TO BUILD ALL THE CENTERLINE POINTS BEFORE THIS! + //It's sufficient to build the outline TOGETHER with the centeraline, for each quadratic! + buildOutline(stroke, cPoints, outline, data); +} + +//============================================================================================ + +TRectD TOutlineUtil::computeBBox(const TStroke &stroke) +{ + typedef TStroke::OutlineOptions OOpts; + + //First, calculate the usual stroke bbox + TRectD roundBBox(::computeBBox(stroke)); + const OOpts &oOptions(stroke.outlineOptions()); + + if (oOptions.m_capStyle != OOpts::PROJECTING_CAP && oOptions.m_joinStyle != OOpts::MITER_JOIN) + return roundBBox; + + //Build interesting centerline points (in this case, junction points) + std::vector cPoints; + int i, chunksCount = stroke.getChunkCount(); + for (i = 0; i < chunksCount; ++i) { + CenterlinePoint cPoint(i, 0.0); + + cPoint.buildPos(stroke); + cPoint.buildDirs(stroke); + cPoints.push_back(cPoint); + } + + //Build the final point. + CenterlinePoint last(chunksCount - 1, 1.0); + + last.buildPos(stroke); + last.buildDirs(stroke); + + //In the selfLoop case, use its info to modify the initial point. + if (stroke.isSelfLoop()) { + CenterlinePoint &first = cPoints[0]; + + first.m_prevD = last.m_prevD; + first.m_hasPrevD = last.m_hasPrevD; + last.m_nextD = first.m_nextD; + last.m_hasNextD = first.m_hasNextD; + first.m_covered = last.m_covered = (first.m_covered && last.m_covered); + } + + cPoints.push_back(last); + + //Now, add the associated 'extending' outline points + OutlineBuilder outBuilder(OutlinizationData(), stroke); + TRectD extensionBBox( + (std::numeric_limits::max)(), + (std::numeric_limits::max)(), + -(std::numeric_limits::max)(), + -(std::numeric_limits::max)()); + + unsigned int j, cPointsCount = cPoints.size(); + for (j = 0;; ++j) { + //Search the next uncovered point + for (; j < cPointsCount && cPoints[j].m_covered; ++j) + ; + + if (j >= cPointsCount) + break; + + //Build the associated outline points + outBuilder.buildOutlineExtensions(extensionBBox, cPoints[j]); + } + + //Finally, merge the 2 bboxes + return roundBBox + extensionBBox; +} diff --git a/toonz/sources/common/tvrender/tellipticbrushP.h b/toonz/sources/common/tvrender/tellipticbrushP.h new file mode 100644 index 0000000..e7309b2 --- /dev/null +++ b/toonz/sources/common/tvrender/tellipticbrushP.h @@ -0,0 +1,275 @@ + + +#ifndef TELLIPTIC_BRUSH_P_H +#define TELLIPTIC_BRUSH_P_H + +#include "tcurves.h" +#include "tstroke.h" +#include "tgl.h" //tglPixelSize +#include "tstrokeoutline.h" + +namespace tellipticbrush +{ + +//!Tolerance parameter used somewhere throughout this code. +const double tolPar = 1e-6; + +//******************************************************************************** +// Geometric Helper Functions +//******************************************************************************** + +double dist(const TPointD &P1, const TPointD &P2); +double dist(const TThickPoint &P1, const TThickPoint &P2); +double angle(const TPointD &v1, const TPointD &v2); + +TPointD intersectionCoords(const TPointD &P0, const TPointD &d0, const TPointD &P1, const TPointD &d1, + double detTol = 1e-2); + +void buildEnvelopeDirection(const TThickPoint &p, const TThickPoint &d, bool left, TPointD &res); +void buildEnvelopeDirections(const TThickPoint &p, const TThickPoint &d, TPointD &resL, TPointD &resR); +void buildEnvelopeVector(const TThickPoint &p, const TThickPoint &d, bool left, TPointD &res); +void buildEnvelopeVectors(const TThickPoint &p, const TThickPoint &d, TPointD &resL, TPointD &resR); + +void buildAngularSubdivision(double radius, double angle, double err, int &nAngles); + +TRectD computeBBox(const TStroke &stroke); + +//******************************************************************************** +// Options structure +//******************************************************************************** + +/* + Structure needed to hold both external and internal outlinization parameters. +*/ +struct OutlinizationData { + TOutlineUtil::OutlineParameter m_options; + double m_pixSize; + + OutlinizationData() : m_options(), m_pixSize(0.0) {} + OutlinizationData(const TOutlineUtil::OutlineParameter &options) + : m_options(options), m_pixSize(sqrt(tglGetPixelSize2())) + { + } +}; + +//******************************************************************************** +// Centerline Point struct +//******************************************************************************** + +/*! + CenterlinePoint contains the data a about a discretized centerline stroke point - + which includes its position, and eventual forward and backward derivative-like + directions. Thickness data is included in the structure. + + These informations are necessary and sufficient to build associated outline points, + and eventual additional points related to caps. +*/ +struct CenterlinePoint { + int m_chunkIdx; //!< The quadratic chunk index containing this point + double m_t; //!< The quadratic parameter where this point can be found + + TThickPoint m_p; //!< The point's thick coordinates + bool m_posBuilt; //!< Wheteher m_p has been calculated + + TThickPoint m_prevD; //!< The backward direction + bool m_hasPrevD; //!< If the point has (envelopable) backward direction + + TThickPoint m_nextD; //!< The forward direction + bool m_hasNextD; //!< If the point has (envelopable) forward direction + + bool m_dirsBuilt; //!< Whether directions have been calculated + + bool m_covered; //!< Whether this point's outline can't be seen + + int m_countIdx; //!< Additional index needed by some procedural style... + + CenterlinePoint() : m_chunkIdx(-1), m_posBuilt(false), m_dirsBuilt(false) {} + CenterlinePoint(int chunk, double t) : m_chunkIdx(chunk), m_t(t), m_posBuilt(false), m_dirsBuilt(false), m_countIdx(0) {} + + ~CenterlinePoint() {} + + void buildPos(const TStroke &stroke); + void buildDirs(const TStroke &stroke); + + bool operator<(const CenterlinePoint &cp) const + { + return m_chunkIdx < cp.m_chunkIdx ? true : m_chunkIdx > cp.m_chunkIdx ? false : m_t < cp.m_t; + } +}; + +//******************************************************************************** +// Linearizator Classes +//******************************************************************************** + +/*! + The StrokeLinearizator class models a stroke linearization interface that + extracts points of interest from a succession of stroke quadratics. +*/ +class StrokeLinearizator +{ +protected: + const TStroke *m_stroke; + +public: + StrokeLinearizator(const TStroke *stroke) : m_stroke(stroke) {} + virtual ~StrokeLinearizator() {} + + /*! + Adds interesting stroke points to be discretized in the + chunk-th TThickQuadratic stroke. + + \note The initial point (P0) of the quadratic is always added by the + outlinization algorithm before these linearization functions are invoked + (whereas P2 belongs to the next quadratic). + */ + virtual void linearize(std::vector &cPoints, int chunk) = 0; +}; + +//******************************************************************************** +// Outline Builder classes +//******************************************************************************** + +/*! + The OutlineBuilder class is the object used to translate centerline points + into outline points. The purpose of this class is to take a CenterlinePoint + instance and build a couple of outline points - at either side of the + centerline - eventually adding (cap) points to form a smooth outline. +*/ +class OutlineBuilder +{ + double m_pixSize; + TStroke::OutlineOptions m_oOptions; + + int m_lastChunk; + +private: + typedef void (OutlineBuilder::*OutlineBuilderFunc)( + std::vector &outPoints, const CenterlinePoint &cPoint); + + OutlineBuilderFunc m_addBeginCap; + OutlineBuilderFunc m_addEndCap; + OutlineBuilderFunc m_addSideCaps; + + typedef void (OutlineBuilder::*BBoxBuilderFunc)( + TRectD &bbox, const CenterlinePoint &cPoint); + + BBoxBuilderFunc m_addBeginCap_ext; + BBoxBuilderFunc m_addEndCap_ext; + BBoxBuilderFunc m_addSideCaps_ext; + +private: + /* + Type-specific outline container functions. + Used with outline building sub-routines that may be used to supply + different outline container types. + For example, a TRectD may be considered a container class to be used + when building the outline bbox. + */ + template + void addEnvelopePoint(T &container, const TPointD &oPoint, int countIdx = 0); + template + void addExtensionPoint(T &container, const TPointD &oPoint, int countIdx = 0); + template + void addOutlineBuilderFunc(OutlineBuilder::OutlineBuilderFunc func, + T &container, const CenterlinePoint &cPoint); + +public: + OutlineBuilder(const OutlinizationData &data, const TStroke &stroke); + ~OutlineBuilder() {} + + /*! + Transforms the specified centerline point into outline points, and adds them to + the supplied outline points vector. + */ + void buildOutlinePoints( + std::vector &outPoints, const CenterlinePoint &cPoint); + + void buildOutlineExtensions(TRectD &bbox, const CenterlinePoint &cPoint); + +private: + void addCircle(std::vector &oPoints, const CenterlinePoint &cPoint); + void addCircularArcPoints( + int idx, std::vector &outPoints, + const TPointD ¢er, const TPointD &ray, double angle, int nAngles, + int countIdx); + + void addRoundBeginCap(std::vector &oPoints, const CenterlinePoint &cPoint); + void addRoundEndCap(std::vector &oPoints, const CenterlinePoint &cPoint); + + void addButtBeginCap(std::vector &oPoints, const CenterlinePoint &cPoint); + void addButtEndCap(std::vector &oPoints, const CenterlinePoint &cPoint); + + template + void addProjectingBeginCap(T &oPoints, const CenterlinePoint &cPoint); + template + void addProjectingEndCap(T &oPoints, const CenterlinePoint &cPoint); + + void addRoundSideCaps(std::vector &oPoints, const CenterlinePoint &cPoint); + void addBevelSideCaps(std::vector &oPoints, const CenterlinePoint &cPoint); + template + void addMiterSideCaps(T &oPoints, const CenterlinePoint &cPoint); +}; + +//******************************************************************************** +// Explicit specializations of OutlineBuilder's methods +//******************************************************************************** + +// Container of Outline Points (for common outline extraction) + +template <> +inline void OutlineBuilder::addEnvelopePoint(std::vector &oPoints, + const TPointD &oPoint, int countIdx) +{ + oPoints.push_back(TOutlinePoint(oPoint, countIdx)); +} + +template <> +inline void OutlineBuilder::addExtensionPoint(std::vector &oPoints, + const TPointD &oPoint, int countIdx) +{ + oPoints.push_back(TOutlinePoint(oPoint, countIdx)); +} + +template <> +inline void OutlineBuilder::addOutlineBuilderFunc( + OutlineBuilder::OutlineBuilderFunc func, + std::vector &oPoints, const CenterlinePoint &cPoint) +{ + (this->*func)(oPoints, cPoint); +} + +//============================================================================================ + +// Rect (for bounding box extraction) + +template <> +inline void OutlineBuilder::addEnvelopePoint(TRectD &bbox, const TPointD &oPoint, int countIdx) +{ +} + +template <> +inline void OutlineBuilder::addExtensionPoint(TRectD &bbox, const TPointD &oPoint, int countIdx) +{ + bbox.x0 = tmin(bbox.x0, oPoint.x); + bbox.y0 = tmin(bbox.y0, oPoint.y); + bbox.x1 = tmax(bbox.x1, oPoint.x); + bbox.y1 = tmax(bbox.y1, oPoint.y); +} + +template <> +inline void OutlineBuilder::addOutlineBuilderFunc( + OutlineBuilder::OutlineBuilderFunc func, + TRectD &container, const CenterlinePoint &cPoint) +{ +} + +//******************************************************************************** +// Standard Outline Builder (from Centerline Points) +//******************************************************************************** + +void buildOutline(const TStroke &stroke, std::vector &cPoints, + TStrokeOutline &outline, const OutlinizationData &data); + +} //namespace tellipticbrush + +#endif //TELLIPTIC_BRUSH_P_H diff --git a/toonz/sources/common/tvrender/tflash.cpp b/toonz/sources/common/tvrender/tflash.cpp new file mode 100644 index 0000000..67f2973 --- /dev/null +++ b/toonz/sources/common/tvrender/tflash.cpp @@ -0,0 +1,2806 @@ + + +#include "tflash.h" +//#include "tstroke.h" +#include "tcurves.h" +#include "tregion.h" +#include "tstrokeprop.h" +#include "tregionprop.h" + +#include "tpalette.h" +#include "tvectorimage.h" +#include "tmachine.h" +#include "trasterimage.h" +#include "tsimplecolorstyles.h" +#include "tcolorfunctions.h" +#include "F3SDK.h" +#include "FFixed.h" +#include "tsop.h" +#include "tropcm.h" +#include "tsweepboundary.h" +#include "tiio_jpg_util.h" +#include "zlib.h" +//#include "trop.h" +#include "ttoonzimage.h" +#include "tconvert.h" +#include "timage_io.h" +#include "tsystem.h" +#include +#include + +#if !defined(TNZ_LITTLE_ENDIAN) +TNZ_LITTLE_ENDIAN undefined !! +#endif + + int Tw = 0; + +bool areTwEqual(double x, double y) +{ + assert(Tw != 0); + + return (int)(Tw * x) == (int)(Tw * y); +} + +bool areTwEqual(TPointD p0, TPointD p1) +{ + assert(Tw != 0); + + return areTwEqual(p0.x, p1.x) && areTwEqual(p0.y, p1.y); +} +//------------------------------------------------------------------- + +const wstring TFlash::ConstantLines = L"Low: Constant Thickness"; +const wstring TFlash::MixedLines = L"Medium: Mixed Thickness"; +const wstring TFlash::VariableLines = L"High: Variable Thickness"; + +Tiio::SwfWriterProperties::SwfWriterProperties() + : m_lineQuality("Curve Quality"), m_isCompressed("File Compression", true), m_autoplay("Autoplay", true), m_looping("Looping", true), m_jpgQuality("Jpg Quality", 0, 100, 90), m_url("URL", wstring()), m_preloader("Insert Preloader", false) +{ + m_lineQuality.addValue(TFlash::MixedLines); + m_lineQuality.addValue(TFlash::ConstantLines); + m_lineQuality.addValue(TFlash::VariableLines); + + bind(m_lineQuality); + bind(m_isCompressed); + bind(m_autoplay); + bind(m_looping); + bind(m_jpgQuality); + bind(m_url); + bind(m_preloader); + + TEnumProperty::Range range = m_lineQuality.getRange(); +} + +//------------------------------------------------------------------- + +enum PolyType { None, + Centerline, + Solid, + Texture, + LinearGradient, + RadialGradient }; + +class PolyStyle +{ +public: + PolyType m_type; + TPixel32 m_color1; //only if type!=Texture + TPixel32 m_color2; //only if type==LinearGradient || type==RadialGradient + double m_smooth; //only if type==RadialGradient + double m_thickness; //only if type==Centerline + TAffine m_matrix; //only if type==Texture + TRaster32P m_texture; //only if type==Texture + //bool m_isRegion; //only if type!=Centerline + //bool m_isHole; //only if type!=Centerline && m_isRegion==true + PolyStyle() : m_type(None), m_color1(), m_color2(), m_smooth(0), m_thickness(0), m_matrix(), m_texture() /*, m_isRegion(false), m_isHole(false)*/ {} + bool operator==(const PolyStyle &p) const; + bool operator<(const PolyStyle &p) const; +}; + +class FlashPolyline +{ +public: + UINT m_depth; + bool m_skip; + bool m_toBeDeleted; + bool m_isPoint; + vector m_quads; + PolyStyle m_fillStyle1; + PolyStyle m_fillStyle2; + PolyStyle m_lineStyle; + //PolyStyle m_bgStyle; + FlashPolyline() : m_depth(0), m_skip(false), m_toBeDeleted(false), m_isPoint(false), m_fillStyle1(), m_fillStyle2(), m_lineStyle() {} + bool operator<(const FlashPolyline &p) const { return m_depth < p.m_depth; } +}; + +class biPoint +{ +public: + TPointD p0, p1; + + biPoint(TPointD _p0, TPointD _p1) : p0(_p0), p1(_p1) {} + biPoint() {} + bool operator<(const biPoint &b) const + { + biPoint aux; + aux.p0.x = areTwEqual(p0.x, b.p0.x) ? p0.x : b.p0.x; + aux.p0.y = areTwEqual(p0.y, b.p0.y) ? p0.y : b.p0.y; + aux.p1.x = areTwEqual(p1.x, b.p1.x) ? p1.x : b.p1.x; + aux.p1.y = areTwEqual(p1.y, b.p1.y) ? p1.y : b.p1.y; + + return (p0.x == aux.p0.x) ? ((p0.y == aux.p0.y) ? ((p1.x == aux.p1.x) ? (p1.y < aux.p1.y) : (p1.x < aux.p1.x)) : (p0.y < aux.p0.y)) : p0.x < aux.p0.x; + } + void revert() { tswap(p0, p1); } +}; + +class wChunk +{ +public: + double w0, w1; + wChunk(double _w0, double _w1) : w0(_w0), w1(_w1) {} + bool operator<(const wChunk &b) const { return (w1 < b.w0); } +}; + +//------------------------------------------------------------------- + +const int c_soundRate = 5512; // 5512; //11025 +const int c_soundBps = 16; +const bool c_soundIsSigned = false; +const int c_soundChannelNum = 1; +const int c_soundCompression = 3; //per compatibilita' con MAC!!! + +//------------------------------------------------------------------- + +class FlashImageData +{ +public: + FlashImageData(TAffine aff, TImageP img, const TColorFunction *cf, bool isMask, bool isMasked) + : m_aff(aff), m_img(img), m_cf(cf), m_isMask(isMask), m_isMasked(isMasked) + { + assert(!isMask || !isMasked); + } + TAffine m_aff; + const TColorFunction *m_cf; + bool m_isMask, m_isMasked; + TImageP m_img; +}; + +class FlashColorStyle +{ +public: + TPixel m_color; + double m_thickness; + U32 m_id; + FlashColorStyle(TPixel color, double thickness, U32 id) + : m_color(color), m_thickness(thickness), m_id(id) {} +}; + +class TFlash::Imp +{ +public: + //double m_totMem; + bool m_supportAlpha; + int m_tw; + UCHAR m_version; + SCOORD m_sCoord1; + bool m_loaderAdded; + TAffine m_globalScale; + //typedef triple FlashImageData; + typedef vector FrameData; + FObjCollection m_tags; + FDTSprite *m_currSprite; + int m_currDepth; + int m_frameRate; + int m_currFrameIndex; + int m_lx, m_ly; + //ouble cameradpix, cameradpiy, inchFactor; + const TPalette *m_currPalette; + int m_soundRate; + Tiio::SwfWriterProperties m_properties; + + bool m_maskEnabled; + bool m_isMask; + + bool m_keepImages; + + std::list m_polylinesArray; + //std::set m_currentEdgeArray; + + TPixel32 m_lineColor; + double m_thickness; + + PolyStyle m_polyData; + //vector m_currentBgStyle; + int m_regionDepth; + int m_strokeCount; + /*TPixel32 m_fillColor; + TAffine m_fillMatrix; + TRaster32P m_texture; + GradientType m_gradientType; + TPixel32 m_gradientColor1, m_gradientColor2;*/ + //std::ofstream m_of; + + TAffine m_affine; + vector m_matrixStack; + map m_imagesMap; + map m_imagesScaleMap; + map m_edgeMap; + map m_texturesMap; + map m_autocloseMap; + map> m_strokeMap; + + vector m_outlines; + TPixel m_currStrokeColor; + //std::set m_outlineColors; + + FrameData *m_frameData; + FrameData *m_oldFrameData; + //bool m_notClipped; + vector m_sound; + int m_soundSize; + vector m_soundBuffer; + int m_soundOffset; + TVectorImageP m_currMask; + + vector *> m_toBeDeleted; + vector m_quadsToBeDeleted; + vector m_strokesToBeDeleted; + void drawPolygon(const vector &poly, bool isOutline); + int setFill(FDTDefineShape3 *shape); + inline FMatrix *affine2Matrix(const TAffine &aff); + void drawHangedObjects(); + void setStyles(const list &polylines, + vector &lineStyleID, vector &fillStyle1ID, vector &fillStyle2ID, + FDTDefineShape3 *polygon); + + U32 findStyle(const PolyStyle &p, std::map &idMap, FDTDefineShape3 *polygon); + void addEdge(const TEdge &e, TPointD &p0, TPointD &p1); + void addNewEdge(const TEdge &e); + //void closeRegion(int numEdges); + + void drawHangedOutlines(); + + void addAutoclose(biPoint &bp, int edgeIndex); + + inline TPoint toTwips(const TPointD &p) { return TPoint((int)(m_tw * p.x), (int)(m_tw * (-p.y))); } + + ~Imp() + { + clearPointerContainer(m_toBeDeleted); + clearPointerContainer(m_quadsToBeDeleted); + clearPointerContainer(m_strokesToBeDeleted); + if (m_oldFrameData) + delete m_oldFrameData; + + while (!m_soundBuffer.empty()) { + delete[] * m_soundBuffer.rbegin(); + m_soundBuffer.pop_back(); + } + } + + //=================================================================== + + /* + l'inizializzazione di m_currDepth e' 3 poiche' si riservanola depth 1 + per la clipcamera e la depth 2 per l'eventuale bottone (non visibile) + del play non automatico +*/ + Imp(int lx, int ly, int frameCount, int frameRate, TPropertyGroup *properties, bool keepImages) + : m_version(4), m_tags(), m_currSprite(0), m_currDepth(3), m_frameRate(frameRate), m_currFrameIndex(-1), m_lx(lx), m_ly(ly), m_currPalette(0), m_maskEnabled(false), m_isMask(false), m_polylinesArray(), m_lineColor(TPixel32::Black), m_thickness(0), m_polyData(), m_regionDepth(0), m_strokeCount(0), m_affine(), m_matrixStack(), m_imagesMap(), m_imagesScaleMap(), m_edgeMap(), m_texturesMap(), m_autocloseMap(), m_strokeMap(), m_outlines(), m_currStrokeColor(0, 0, 0, 0), m_frameData(0), m_oldFrameData(0) + //, m_notClipped(true) + , + m_sound(), m_soundSize(0), m_soundBuffer(), m_currMask(), m_toBeDeleted(), m_quadsToBeDeleted(), m_strokesToBeDeleted(), m_soundOffset(0), m_loaderAdded(false), m_globalScale(), m_keepImages(keepImages), m_supportAlpha(true), m_soundRate(c_soundRate) + //, m_totMem(0) + + //, m_of("c:\\temp\\boh.txt") + + { + m_tags.AddFObj(new FCTFrameLabel(new FString("DigitalVideoRm"))); + + if (properties) + m_properties.setProperties(properties); + //m_currentBgStyle.push_back(PolyStyle()); + + m_tw = 16384 / tmax(m_lx, m_ly); + if (m_tw > 20) + m_tw = 20; + Tw = m_tw; + m_sCoord1 = m_tw; + //addCameraClip(); + if (!m_properties.m_autoplay.getValue() && !m_properties.m_preloader.getValue()) + addPause(); + } + + void drawSubregions(TFlash *tf, const TRegion *r, const TPalette *palette); + void doDrawPolygon(list &polylines, int clippedShapes = 0); + int drawSegments(const vector segmentArray, bool isGradientColor); + int drawquads(const vector quadsArray); + int drawRectangle(const TRectD &rect); + int drawPolyline(vector &poly); + int drawEllipse(const TPointD ¢er, double radiusX, double radiusY); + void drawDot(const TPointD ¢er, double radius); + + void buildRegion(TFlash *tf, const TVectorImageP &vi, int regionIndex); + void buildStroke(TFlash *tf, const TVectorImageP &vi, int strokeIndex); + + //void addCameraClip(int index); + void writeFrame(TFlash *tf, bool isLast, int frameCountLoader, bool lastScene); + U16 getTexture(const PolyStyle &p, int &lx, int &ly); + + void addSoundToFrame(bool isLast); + + void addActionStop(); + void addLoader(); + void addSkipLoader(int jumpToFrame); + void addPause(); + void beginMask(); + void endMask(); + void addUrlLink(string url); + USHORT buildImage(const TImageP vi, TFlash *tf, double &scaleFactor, bool isMask); + USHORT buildVectorImage(const TVectorImageP &img, TFlash *tf, double &scaleFactor, bool isMask); + USHORT buildRasterImage(const TImageP rimg, TFlash *tf); + bool drawOutline(TStroke *s, bool separeDifferentColors = true); + inline void addEdgeStraightToShape(FDTDefineShape3 *shape, int x, int y); + inline void addEdgeStraightToShape(FDTDefineShape3 *shape, const TPoint &p); +}; + +//=================================================================== + +void TFlash::setSoundRate(int soundrate) +{ + m_imp->m_soundRate = soundrate; +} + +//=================================================================== + +void TFlash::enableAlphaChannelForRaster(bool supportAlpha) +{ + m_imp->m_supportAlpha = supportAlpha; +} + +//=================================================================== +namespace +{ +inline void addShape(FDTDefineShape3 *polygon, bool newStyle, bool lStyle, + bool fillStyle1, bool fillStyle0, bool move, int x, int y, + int style0, int style1, int lineStyle) +{ + polygon->AddShapeRec(new FShapeRecChange(newStyle, lStyle, fillStyle1, fillStyle0, move, + x, y, style0, style1, lineStyle, 0, 0)); +} + +inline void addShape(FDTDefineShape3 *polygon, bool newStyle, bool lStyle, + bool fillStyle1, bool fillStyle0, bool move, TPoint *p, + int style0, int style1, int lineStyle) +{ + polygon->AddShapeRec(new FShapeRecChange(newStyle, lStyle, fillStyle1, fillStyle0, move, + p ? p->x : 0, p ? p->y : 0, style0, style1, lineStyle, 0, 0)); +} +} + +//=================================================================== + +inline void TFlash::Imp::addEdgeStraightToShape(FDTDefineShape3 *shape, int x, int y) +{ + if (x == 0 && y == 0) + return; + + //m_of<< "ADD STRAIGHT LINE: "< 65535 || abs(y) > 65535) //flash non sa scrivere segmenti piu' lunghi di cosi', spezzo + { + shape->AddShapeRec(new FShapeRecEdgeStraight((x + 1) / 2, (y + 1) / 2)); + shape->AddShapeRec(new FShapeRecEdgeStraight(x / 2, y / 2)); + } else + shape->AddShapeRec(new FShapeRecEdgeStraight(x, y)); +} + +inline void TFlash::Imp::addEdgeStraightToShape(FDTDefineShape3 *shape, const TPoint &p) +{ + addEdgeStraightToShape(shape, p.x, p.y); +} + +//------------------------------------------------------------------- + +double computeAverageThickness(const TStroke *s) +{ + int count = s->getControlPointCount(); + double resThick = 0; + int i; + + for (i = 0; i < s->getControlPointCount(); i++) { + double thick = s->getControlPoint(i).thick; + if (i >= 2 && i < s->getControlPointCount() - 2) + resThick += thick; + } + if (count < 6) + return s->getControlPoint(count / 2 + 1).thick; + + return resThick / (s->getControlPointCount() - 4); +} + +//------------------------------------------------------------------- + +inline FMatrix *TFlash::Imp::affine2Matrix(const TAffine &aff) +{ + if (aff != TAffine()) { + bool hasA11OrA22, hasA12OrA21; + + hasA11OrA22 = hasA12OrA21 = (aff.a12 != 0 || aff.a21 != 0); + if (!hasA12OrA21) + hasA11OrA22 = !areAlmostEqual(aff.det(), 1.0, 1e-3); + return new FMatrix(hasA11OrA22, FloatToFixed(aff.a11), FloatToFixed(aff.a22), + hasA12OrA21, FloatToFixed(-aff.a21), FloatToFixed(-aff.a12), + (TINT32)tround(aff.a13 * m_tw), -(TINT32)tround(aff.a23 * m_tw)); + } else + return 0; +} + +//------------------------------------------------------------------- + +int TFlash::Imp::drawSegments(const vector segmentArray, bool isGradientColor) +{ + int i; + assert(m_currSprite); + + if (segmentArray.empty()) + return 0; + + TPointD firstPoint = segmentArray[0].getP0(); + + FDTDefineShape3 *polygon = new FDTDefineShape3(); + + U32 lineStyleID1, lineStyleID2, lineStyleID4; + lineStyleID1 = polygon->AddLineStyle((int)m_thickness * m_tw, new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, m_lineColor.m)); + if (isGradientColor) { + lineStyleID2 = polygon->AddLineStyle((int)m_thickness * m_tw, new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, int(0.50 * (m_lineColor.m)))); + //U32 lineStyleID3 = polygon->AddLineStyle(m_tw, new FColor(m_color.r, m_color.g, m_color.b, int(0.25*(m_color.m)))); + lineStyleID4 = polygon->AddLineStyle((int)m_thickness * m_tw, new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, int(0.125 * (m_lineColor.m)))); + } + polygon->FinishStyleArrays(); + + TRectD box; + box.x0 = firstPoint.x; + box.x1 = firstPoint.x; + box.y0 = firstPoint.y; + box.y1 = firstPoint.y; + + for (i = 0; i < (int)segmentArray.size(); i++) { + box += segmentArray[i].getBBox(); + TPoint p0 = convert(segmentArray[i].getP0()); + TPoint dp = convert(segmentArray[i].getP1()) - p0; + TPoint p((int)(m_tw * p0.x), (int)(-m_tw * p0.y)); + addShape(polygon, false, isGradientColor || (i == 0), false, false, true, &p, 0, + 0, (isGradientColor || (i == 0)) ? lineStyleID1 : 0); + + if (isGradientColor) + addEdgeStraightToShape(polygon, 5 * dp.x, -5 * dp.y); + else { + addEdgeStraightToShape(polygon, (int)(m_tw * dp.x), (int)(m_tw * -dp.y)); + continue; + } + + addShape(polygon, false, true, false, false, false, 0, 0, 0, lineStyleID2); + + addEdgeStraightToShape(polygon, 5 * dp.x, -5 * dp.y); + + addShape(polygon, false, true, false, false, false, 0, 0, 0, lineStyleID4); + + addEdgeStraightToShape(polygon, 5 * dp.x, -5 * dp.y); + } + + polygon->AddShapeRec(new FShapeRecEnd()); + polygon->setBounds(new FRect((int)(m_tw * box.x0), -(int)(m_tw * box.y0), (int)(m_tw * box.x1), -(int)(m_tw * box.y1))); + + m_tags.AddFObj(polygon); + + FCTPlaceObject2 *placePolygon = new FCTPlaceObject2(false, // ~ _hasClipDepth + false, true, false, + m_currDepth++, polygon->ID(), affine2Matrix(m_affine), 0, 0, 0, 0 /**/); + + m_currSprite->AddFObj(placePolygon); + return polygon->ID(); +} + +//------------------------------------------------------------------- + +int TFlash::Imp::drawquads(const vector quadsArray) +{ + int i; + assert(m_currSprite); + if (quadsArray.empty()) + return 0; + + TPointD firstPoint = quadsArray[0].getP0(); + + TRectD box; + box.x0 = firstPoint.x; + box.x1 = firstPoint.x; + box.y0 = firstPoint.y; + box.y1 = firstPoint.y; + + for (i = 0; i < (int)quadsArray.size(); i++) + box += quadsArray[i].getBBox(); + + FDTDefineShape3 *polygon = new FDTDefineShape3(new FRect((int)(m_tw * box.x0), -(int)(m_tw * box.y0), (int)(m_tw * box.x1), -(int)(m_tw * box.y1))); + + U32 lineStyleID; + lineStyleID = polygon->AddLineStyle(m_tw, new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, m_lineColor.m)); + + polygon->FinishStyleArrays(); + + for (i = 0; i < (int)quadsArray.size(); i++) { + TPoint p0 = toTwips(quadsArray[i].getP0()); + + addShape(polygon, false, i == 0, false, false, true, &p0, 0, 0, (i == 0) ? lineStyleID : 0); + + TPoint dp1 = convert((quadsArray[i].getP1() - quadsArray[i].getP0())); + TPoint dp2 = convert((quadsArray[i].getP2() - quadsArray[i].getP1())); + + if ((dp1 == TPoint())) { + if (dp2 == TPoint()) + continue; + addEdgeStraightToShape(polygon, (int)(m_tw * dp2.x), (int)(m_tw * -dp2.y)); + } else if ((dp2 == TPoint())) + addEdgeStraightToShape(polygon, (int)(m_tw * dp1.x), (int)(m_tw * -dp1.y)); + else + polygon->AddShapeRec(new FShapeRecEdgeCurved((int)(m_tw * dp1.x), (int)(m_tw * -dp1.y), (int)(m_tw * dp2.x), (int)(m_tw * -dp2.y))); + } + + polygon->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(polygon); + + FCTPlaceObject2 *placePolygon = new FCTPlaceObject2(false, // ~ _hasClipDepth + false, true, false, + m_currDepth++, polygon->ID(), affine2Matrix(m_affine), 0, 0, 0, 0 /**/); + + m_currSprite->AddFObj(placePolygon); + return polygon->ID(); +} + +//------------------------------------------------------------------- + +void putquads(const TStroke *s, double w0, double w1, vector &quads) +{ + int chunkIndex0, chunkIndex1, i; + double dummy; + bool ret; + + ret = s->getChunkAndT(w0, chunkIndex0, dummy); + assert(!ret); + ret = s->getChunkAndT(w1, chunkIndex1, dummy); + assert(!ret); + assert(chunkIndex0 <= chunkIndex1); + + for (i = chunkIndex0; i <= chunkIndex1; i++) + quads.push_back((TQuadratic *)s->getChunk(i)); +} + +//------------------------------------------------------------------- + +void computeOutlineBoundary(vector &outlines, list &polylinesArray, const TPixel &color) +{ + UINT size = polylinesArray.size(); + + vector> quads; + computeSweepBoundary(outlines, quads); + + outlines.clear(); + std::list::iterator it = polylinesArray.begin(); + std::advance(it, size); + for (int i = 0; i < (int)quads.size(); i++) { + vector &q = quads[i]; + + polylinesArray.push_back(FlashPolyline()); + polylinesArray.back().m_quads = quads[i]; + polylinesArray.back().m_toBeDeleted = true; + polylinesArray.back().m_fillStyle1.m_type = Solid; + polylinesArray.back().m_fillStyle1.m_color1 = color; + } +} + +//------------------------------------------------------------------- + +void TFlash::Imp::drawHangedObjects() +{ + //int i=0; + + if (!m_outlines.empty()) + computeOutlineBoundary(m_outlines, m_polylinesArray, m_currStrokeColor); + + m_currStrokeColor = TPixel::Transparent; + //m_outlineColors.clear(); + + if (!m_polylinesArray.empty()) { + doDrawPolygon(m_polylinesArray, false); + + std::list::iterator it; + for (it = m_polylinesArray.begin(); it != m_polylinesArray.end(); ++it) + if (it->m_toBeDeleted) + clearPointerContainer(it->m_quads); + m_polylinesArray.clear(); + m_edgeMap.clear(); + m_autocloseMap.clear(); + m_strokeMap.clear(); + } + + clearPointerContainer(m_strokesToBeDeleted); +} + +//------------------------------------------------------------------- + +int TFlash::Imp::drawPolyline(vector &poly) +{ + TRect box(1000, 1000, -1000, -1000); + + int i; + + FDTDefineShape3 *polyLine = new FDTDefineShape3(); + U16 id = polyLine->ID(); + + U32 fillID = setFill(polyLine); + + U32 lineStyleID = 0; + + if (m_thickness > 0) + lineStyleID = polyLine->AddLineStyle((int)(m_thickness * m_tw), new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, m_lineColor.m)); + + polyLine->FinishStyleArrays(); + + TPointD currP = TPointD(m_tw * poly[0].x, -m_tw * poly[0].y); + TPoint oldIntCurrP, intCurrP = convert(currP); + + addShape(polyLine, false, lineStyleID != 0, false, fillID != 0, true, + &intCurrP, fillID, 0, lineStyleID); + + poly.push_back(poly.front()); //con le approssimazioni,le poly chiuse potrebbero non esserlo + for (i = 0; i < (int)poly.size() - 1; i++) { + currP += TPointD(m_tw * (+poly[i + 1].x - poly[i].x), m_tw * (-poly[i + 1].y + poly[i].y)); + oldIntCurrP = intCurrP; + intCurrP = convert(currP); + if (intCurrP != oldIntCurrP) + addEdgeStraightToShape(polyLine, intCurrP.x - oldIntCurrP.x, intCurrP.y - oldIntCurrP.y); + + if (intCurrP.x > box.x1) + box.x1 = intCurrP.x; + if (intCurrP.x < box.x0) + box.x0 = intCurrP.x; + if (intCurrP.y > box.y1) + box.y1 = intCurrP.y; + if (intCurrP.y < box.y0) + box.y0 = intCurrP.y; + } + poly.pop_back(); //ritolgo, per non alterare il vettore in ingresso alla funzione + + polyLine->AddShapeRec(new FShapeRecEnd()); + polyLine->setBounds(new FRect(box.x0, box.y0, box.x1, box.y1)); + m_tags.AddFObj(polyLine); + + FCTPlaceObject2 *placePoly = new FCTPlaceObject2(false, false, true, false, + m_currDepth++, id, affine2Matrix(m_affine), 0, 0, 0, 0); + + m_currSprite->AddFObj(placePoly); + return id; +} + +//------------------------------------------------------------------- + +int TFlash::Imp::drawRectangle(const TRectD &rect) +{ + TRect box = convert(TRectD(rect.x0 * m_tw, rect.y0 * m_tw, rect.x1 * m_tw, rect.y1 * m_tw)); + + FDTDefineShape3 *rectangle = new FDTDefineShape3(new FRect(box.x0, box.y0, box.x1, box.y1)); + U16 id = rectangle->ID(); + + U32 fillID = setFill(rectangle); + + U32 lineStyleID = 0; + + if (m_thickness > 0) + lineStyleID = rectangle->AddLineStyle((int)(m_thickness * m_tw), new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, m_lineColor.m)); + + rectangle->FinishStyleArrays(); + + addShape(rectangle, false, true, false, fillID != 0, true, + box.x0, -box.y0, fillID, 0, lineStyleID); + + addEdgeStraightToShape(rectangle, box.x1 - box.x0, 0); + addEdgeStraightToShape(rectangle, 0, -box.y1 + box.y0); + addEdgeStraightToShape(rectangle, -box.x1 + box.x0, 0); + addEdgeStraightToShape(rectangle, 0, box.y1 - box.y0); + rectangle->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(rectangle); + + FCTPlaceObject2 *placeRectangle = new FCTPlaceObject2(false, false, true, false, + m_currDepth++, id, affine2Matrix(m_affine), 0, 0, 0, 0); + + m_currSprite->AddFObj(placeRectangle); + return id; +} + +//------------------------------------------------------------------- + +//------------------------------------------------------------------- + +U16 TFlash::Imp::getTexture(const PolyStyle &p, int &lx, int &ly) +{ + assert(p.m_type == Texture); + assert(p.m_texture->getPixelSize() == 4); + lx = p.m_texture->getLx(), ly = p.m_texture->getLy(); + + std::map::iterator it = m_texturesMap.find(p.m_texture.getPointer()); + if (it != m_texturesMap.end()) + return (*it).second; + + assert(p.m_texture->getWrap() == lx); + + std::vector *buffer = new std::vector(); + m_toBeDeleted.push_back(buffer); + Tiio::createJpg(*buffer, p.m_texture, m_properties.m_jpgQuality.getValue()); + FDTDefineBitsJPEG2 *bitmap = new FDTDefineBitsJPEG2((UCHAR *)&(*buffer)[0], buffer->size()); + + m_tags.AddFObj(bitmap); + m_texturesMap[p.m_texture.getPointer()] = bitmap->ID(); + //delete bufout; + return bitmap->ID(); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::drawDot(const TPointD ¢er, double radius) +{ + FlashPolyline quads; + quads.m_lineStyle.m_type = Centerline; + quads.m_lineStyle.m_thickness = radius * 1.5; + quads.m_lineStyle.m_color1 = (m_polyData.m_color1 == TPixel::Transparent) ? m_lineColor : m_polyData.m_color1; + + //quads.m_lineStyle.m_isRegion = false; + //quads.m_lineStyle.m_isHole = false; + + int x = (int)((m_tw * center.x) + 0.5); + x += 3; + TPointD aux = TPointD((double)x / m_tw, center.y); + + quads.m_quads.push_back((TQuadratic *)new TQuadratic(center, 0.5 * (center + aux), aux)); + m_polylinesArray.push_back(quads); +} + +//------------------------------------------------------------------- + +int TFlash::Imp::drawEllipse(const TPointD ¢er, double radiusX, double radiusY) +{ + int xmin = (int)(m_tw * (center.x - radiusX)); // x coordinate of the upper left corner of the bounding rectangle + int ymin = (int)(m_tw * (-center.y - radiusY)); // y coordinate of the upper left corner of the bounding rectangle + int xmax = (int)(m_tw * (center.x + radiusX)); // x coordinate of the bottom right corner of the bounding rectangle + int ymax = (int)(m_tw * (-center.y + radiusY)); // y coordinate of the bottom right corner of the bounding rectangle + int dx = xmax - xmin; // dx is width diameter + int dy = ymax - ymin; // dy is height diameter + + // connect a serie of curves to draw the circle + int c1dx = (int)(0.1465 * dx); + int c1dy = (int)(0.1465 * dy); + int c2dx = (int)(0.2070 * dx); + int c2dy = (int)(0.2070 * dy); + + if (c1dx == 0 || c1dy == 0 || c2dx == 0 || c2dy == 0) + return 0; + + FDTDefineShape3 *ellipse = new FDTDefineShape3(new FRect(xmin, ymin, xmax, ymax)); + + U16 ellipseID = ellipse->ID(); + U32 fillID = setFill(ellipse); + + U32 lineStyleID = 0; + if (m_thickness > 0) + lineStyleID = ellipse->AddLineStyle((int)(2 * m_thickness * m_tw), + new FColor(m_lineColor.r, m_lineColor.g, m_lineColor.b, m_lineColor.m)); + + ellipse->FinishStyleArrays(); + + addShape(ellipse, false, lineStyleID > 0, false, fillID != 0, true, + xmax, -(int)(m_tw * center.y), fillID, 0, lineStyleID); + + ellipse->AddShapeRec(new FShapeRecEdgeCurved(0, -c2dy, -c1dx, -c1dy)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(-c1dx, -c1dy, -c2dx, 0)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(-c2dx, 0, -c1dx, c1dy)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(-c1dx, c1dy, 0, c2dy)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(0, c2dy, c1dx, c1dy)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(c1dx, c1dy, c2dx, 0)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(c2dx, 0, c1dx, -c1dy)); + ellipse->AddShapeRec(new FShapeRecEdgeCurved(c1dx, -c1dy, 0, -c2dy)); + + //TPoint dp1 = convert(m_tw*(outPolyline[0]->getP0()-outPolyline.back()->getP2())); + + ellipse->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(ellipse); + FCTPlaceObject2 *placePolygon = new FCTPlaceObject2(false, // ~ _hasClipDepth + false, true, false, + m_currDepth++, ellipseID, affine2Matrix(m_affine), 0, 0, 0, 0 /**/); + + m_currSprite->AddFObj(placePolygon); + return ellipseID; +} +//------------------------------------------------------------------- +int TFlash::Imp::setFill(FDTDefineShape3 *shape) +{ + if (m_polyData.m_type == Texture) { + int lx, ly; + U16 texId = getTexture(m_polyData, lx, ly); + + FMatrix *app = affine2Matrix(m_polyData.m_matrix * TScale(2048.0 / lx, 2048.0 / ly)); + return shape->AddFillStyle(new FFillStyleBitmap(true, texId, app)); + } else if (m_polyData.m_type == LinearGradient || m_polyData.m_type == RadialGradient) { + FGradient *grad = new FGradient(); + FGradRecord *gradRec1 = new FGradRecord(0, new FColor(m_polyData.m_color1.r, m_polyData.m_color1.g, m_polyData.m_color1.b, m_polyData.m_color1.m)); + FGradRecord *gradRec2 = new FGradRecord(255, new FColor(m_polyData.m_color2.r, m_polyData.m_color2.g, m_polyData.m_color2.b, m_polyData.m_color2.m)); + grad->Add(gradRec1); + grad->Add(gradRec2); + return shape->AddFillStyle(new FFillStyleGradient(m_polyData.m_type == LinearGradient, affine2Matrix(m_polyData.m_matrix * TScale(10.0)), grad)); + } else if (m_polyData.m_type == Solid) { + FColor *color1 = new FColor(m_polyData.m_color1.r, m_polyData.m_color1.g, m_polyData.m_color1.b, m_polyData.m_color1.m); + return shape->AddSolidFillStyle(color1); + } + return 0; +} + +//------------------------------------------------------------------- + +bool PolyStyle::operator==(const PolyStyle &p) const +{ + if (m_type != p.m_type) + return false; + + switch (m_type) { + case Centerline: + return m_thickness == p.m_thickness && m_color1 == p.m_color1; + CASE Solid : return m_color1 == p.m_color1; + CASE Texture : return m_matrix == p.m_matrix && m_texture.getPointer() == p.m_texture.getPointer(); + CASE LinearGradient : __OR RadialGradient : return m_color1 == p.m_color1 && m_color2 == p.m_color2 && m_matrix == p.m_matrix && m_smooth == p.m_smooth; + DEFAULT: + assert(false); + return false; + } +} + +//------------------------------------------------------------------- + +bool affineMinorThen(const TAffine &m0, const TAffine &m1) +{ + if (m0.a11 == m1.a11) { + if (m0.a12 == m1.a12) { + if (m0.a13 == m1.a13) { + if (m0.a21 == m1.a21) { + if (m0.a22 == m1.a22) + return m0.a23 < m1.a23; + else + return m0.a22 < m1.a22; + } else + return m0.a21 < m1.a21; + } else + return m0.a13 < m1.a13; + } else + return m0.a12 < m1.a12; + } else + return m0.a11 < m1.a11; +} + +//------------------------------------------------------------------- + +bool PolyStyle::operator<(const PolyStyle &p) const +{ + if (m_type == p.m_type) + switch (m_type) { + case Centerline: + return (m_thickness == p.m_thickness) ? m_color1 < p.m_color1 : m_thickness < p.m_thickness; + CASE Solid : return m_color1 < p.m_color1; + CASE Texture : return m_texture.getPointer() < p.m_texture.getPointer(); //ignoro la matrice!!!! + CASE LinearGradient : __OR RadialGradient : return (m_smooth == p.m_smooth) ? ((m_color1 == p.m_color1) ? ((m_color2 == p.m_color2) ? affineMinorThen(m_matrix, p.m_matrix) : m_color2 < p.m_color2) : m_color1 < p.m_color1) : m_smooth < p.m_smooth; + DEFAULT: + assert(false); + return false; + } + else + return m_type < p.m_type; +} + +//------------------------------------------------------------------- + +U32 TFlash::Imp::findStyle(const PolyStyle &p, std::map &idMap, + FDTDefineShape3 *polygon) +{ + U32 styleID = 0; + std::map::iterator it; + it = idMap.find(p); + + if (it != idMap.end()) + return (*it).second; + else { + switch (p.m_type) { + case Centerline: { + FColor *color = new FColor(p.m_color1.r, p.m_color1.g, + p.m_color1.b, p.m_color1.m); + int thickness = (int)(2 * p.m_thickness * m_tw); + if (p.m_thickness > 0 && thickness == 0) + thickness = 1; + + styleID = polygon->AddLineStyle(thickness, color); + } + CASE Solid: + { + if (p.m_color1.m == 0) + styleID = 0; + else { + FColor *color = new FColor(p.m_color1.r, p.m_color1.g, + p.m_color1.b, p.m_color1.m); + styleID = polygon->AddSolidFillStyle(color); + } + } + CASE Texture: + { + try { + int lx, ly; + U16 texId = getTexture(p, lx, ly); + FMatrix *app = affine2Matrix(p.m_matrix * TScale(2048.0 / lx, 2048.0 / ly)); + styleID = polygon->AddFillStyle(new FFillStyleBitmap(true, texId, app)); + } catch (TException &) { + FColor *color = new FColor(0, 0, 0, 255); + styleID = polygon->AddSolidFillStyle(color); + } + } + CASE RadialGradient: + { + FGradient *grad = new FGradient(); + //FGradRecord *gradRec1 = new FGradRecord(0, new FColor(p.m_color1.r, p.m_color1.g, p.m_color1.b, p.m_color1.m)); + //FGradRecord *gradRec2 = new FGradRecord(255, new FColor(p.m_color2.r, p.m_color2.g, p.m_color2.b, p.m_color2.m)); + int fac = (int)(127.0 - 0.56 * p.m_smooth); + assert(fac >= 0 && fac < 128); + FGradRecord *gradRec1 = new FGradRecord(fac, new FColor(p.m_color1.r, p.m_color1.g, p.m_color1.b, p.m_color1.m)); + FGradRecord *gradRec2 = new FGradRecord(255 - fac, new FColor(p.m_color2.r, p.m_color2.g, p.m_color2.b, p.m_color2.m)); + grad->Add(gradRec1); + grad->Add(gradRec2); + styleID = polygon->AddFillStyle(new FFillStyleGradient(false, affine2Matrix(p.m_matrix * TScale(15.0)), grad)); + } + CASE LinearGradient: + { + FGradient *grad = new FGradient(); + FGradRecord *gradRec1 = new FGradRecord(0, new FColor(p.m_color1.r, p.m_color1.g, p.m_color1.b, p.m_color1.m)); + FGradRecord *gradRec2 = new FGradRecord(255, new FColor(p.m_color2.r, p.m_color2.g, p.m_color2.b, p.m_color2.m)); + grad->Add(gradRec1); + grad->Add(gradRec2); + styleID = polygon->AddFillStyle(new FFillStyleGradient(true, affine2Matrix(p.m_matrix * TScale(10.0)), grad)); + } + DEFAULT: + assert(false); + } + idMap[p] = styleID; + return styleID; + } +} + +//------------------------------------------------------------------- + +void TFlash::Imp::setStyles(const list &polylines, + vector &lineStyleID, vector &fillStyle1ID, vector &fillStyle2ID, FDTDefineShape3 *polygon) +{ + int i; + std::list::const_iterator it, itOld; + + std::map idMap; + + for (i = 0, it = polylines.begin(); it != polylines.end(); ++i, itOld = it, ++it) { + if (it->m_lineStyle.m_type == None) + lineStyleID[i] = 0; + else if (i > 0 && it->m_lineStyle == itOld->m_lineStyle) + lineStyleID[i] = lineStyleID[i - 1]; + else + lineStyleID[i] = findStyle(it->m_lineStyle, idMap, polygon); + + if (it->m_fillStyle1.m_type == None) + fillStyle1ID[i] = 0; + else if (i > 0 && it->m_fillStyle1 == itOld->m_fillStyle1) + fillStyle1ID[i] = fillStyle1ID[i - 1]; + else + fillStyle1ID[i] = findStyle(it->m_fillStyle1, idMap, polygon); + + if (it->m_fillStyle2.m_type == None) + fillStyle2ID[i] = 0; + else if (i > 0 && it->m_fillStyle2 == itOld->m_fillStyle2) + fillStyle2ID[i] = fillStyle2ID[i - 1]; + else + fillStyle2ID[i] = findStyle(it->m_fillStyle2, idMap, polygon); + } +} + +//------------------------------------------------------------------- + +TPoint drawPoint(const TQuadratic *poly, FDTDefineShape3 *polygon, double radius, TRect &box) +{ + TPointD center = poly->getP0(); + int xmin = (int)(Tw * (center.x - radius)); // x coordinate of the upper left corner of the bounding rectangle + int ymin = (int)(Tw * (-center.y - radius)); // y coordinate of the upper left corner of the bounding rectangle + int xmax = (int)(Tw * (center.x + radius)); // x coordinate of the bottom right corner of the bounding rectangle + int ymax = (int)(Tw * (-center.y + radius)); // y coordinate of the bottom right corner of the bounding rectangle + int dx = xmax - xmin; // dx is width diameter + int dy = ymax - ymin; // dy is height diameter + + // connect a serie of curves to draw the circle + int c1dx = (int)(0.1465 * dx); + int c1dy = (int)(0.1465 * dy); + int c2dx = (int)(0.2070 * dx); + int c2dy = (int)(0.2070 * dy); + + if (c1dx == 0 || c1dy == 0 || c2dx == 0 || c2dy == 0) + return TPoint(); + + if (xmax > box.x1) + box.x1 = xmax; + if (xmin < box.x0) + box.x0 = xmin; + if (ymax > box.y1) + box.y1 = ymax; + if (ymin < box.y0) + box.y0 = ymin; + + //polygon->AddShapeRec(new FShapeRecEdgeCurved(dp1.x, -dp1.y, dp2.x, -dp2.y)); + // + + addShape(polygon, false, true, false, false, true, + xmax, (int)(Tw * -center.y), 0, 0, 0); + + polygon->AddShapeRec(new FShapeRecEdgeCurved(0, -c2dy, -c1dx, -c1dy)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(-c1dx, -c1dy, -c2dx, 0)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(-c2dx, 0, -c1dx, c1dy)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(-c1dx, c1dy, 0, c2dy)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(0, c2dy, c1dx, c1dy)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(c1dx, c1dy, c2dx, 0)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(c2dx, 0, c1dx, -c1dy)); + polygon->AddShapeRec(new FShapeRecEdgeCurved(c1dx, -c1dy, 0, -c2dy)); + + return TPoint(xmax, (int)(Tw * -center.y)); +} + +//------------------------------------------------------------------- +inline void updateBBox(TRect &box, const TPoint &p) +{ + if (p.x > box.x1) + box.x1 = p.x; + if (p.x < box.x0) + box.x0 = p.x; + if (p.y > box.y1) + box.y1 = p.y; + if (p.y < box.y0) + box.y0 = p.y; +} + +void TFlash::Imp::doDrawPolygon(list &polylines, int clippedShapes) +{ + assert(m_currSprite); + assert(!polylines.empty()); + + FDTDefineShape3 *polygon = new FDTDefineShape3(); + U16 polygonID = polygon->ID(); + + //U32 fillID = 0; + + /* +if (isOutline || isRegion) + fillID = ((clippedShapes>0)?polygon->AddSolidFillStyle( new FColor(0, 0, 0)):setFill(polygon)); +*/ + + std::map> idMap; + + vector fillStyle1ID(polylines.size()); + vector fillStyle2ID(polylines.size()); + vector lineStyleID(polylines.size()); + + int i, j; + setStyles(polylines, lineStyleID, fillStyle1ID, fillStyle2ID, polygon); + polygon->FinishStyleArrays(); + + std::list::iterator itOld, it = polylines.begin(), it_e = polylines.end(); + + for (i = 0; it != it_e; ++i, ++it) //le maschere non vengono bene con regioni con fill2 opaco e fill1 trasparente. le giro + if (fillStyle2ID[i] != 0 && fillStyle1ID[i] == 0) { + fillStyle1ID[i] = fillStyle2ID[i]; + fillStyle2ID[i] = 0; + vector &v = (*it).m_quads; + std::reverse(v.begin(), v.end()); + for (j = 0; j < (int)v.size(); j++) { + v[j] = new TQuadratic(v[j]->getP2(), v[j]->getP1(), v[j]->getP0()); + m_quadsToBeDeleted.push_back(v[j]); + } + } + + TPoint lastPoint, firstPoint = toTwips(polylines.front().m_quads[0]->getP0()); + + TPoint currP = TPoint(firstPoint.x, firstPoint.y); + TRect box; + box.x0 = firstPoint.x; + box.x1 = firstPoint.x; + box.y0 = firstPoint.y; + box.y1 = firstPoint.y; + + //const PolyStyle& p = polylines.front().m_fillStyle1; + + //assert(firstPoint.x!=0 && firstPoint.y!=0); + + U32 currLineStyle = 0, currFillStyle1 = 0, currFillStyle2 = 0; + + //if (lineStyleID[0]!=0 && (isOutline||p.m_isRegion)) + currLineStyle = lineStyleID[0]; + + //if (fillStyle1ID[0]!=0 && (isOutline||p.m_isRegion)) + currFillStyle1 = fillStyle1ID[0]; + //if (fillStyle2ID[0]!=0 && p.m_isRegion) + currFillStyle2 = fillStyle2ID[0]; + + //m_of << "DRAW POLYGON da (" << firstPoint.x<< ", "<m_skip) //m_of << " SKIPPOOOOO! " < &poly = it->m_quads; + firstPoint = toTwips(poly[0]->getP0()); + lastPoint = toTwips(poly.back()->getP2()); + /* m_of << " POLYLINE # da ( " + < 0) { + //faccio una move se il salto e' sensato...evito di mettere inutili addShapeRec nell'swf per renderlo piu' piccolo... + + bool isMove = (currP != firstPoint); + + //bool isMove = (currP.x-firstPoint.x)<-20 || (currP.x-firstPoint.x)>20||(currP.y-firstPoint.y)<-20||(currP.y-firstPoint.y)>20; + currP = firstPoint; + + updateBBox(box, currP); + + bool isLineStyle = (lineStyleID[j] != currLineStyle); + + if (isLineStyle) + currLineStyle = lineStyleID[j]; + + bool isFillStyle1 = (fillStyle1ID[j] != currFillStyle1); //se sto passando da una regione a una linea, devo mettere a zero lo stile di fill! + if (isFillStyle1) + currFillStyle1 = fillStyle1ID[j]; + + bool isFillStyle2 = (fillStyle2ID[j] != currFillStyle2); //se sto passando da una regione a una linea, devo mettere a zero lo stile di fill! + if (isFillStyle2) + currFillStyle2 = fillStyle2ID[j]; + assert(firstPoint.x != 0 || firstPoint.y != 0); + + if (isMove || isLineStyle || isFillStyle1 || isFillStyle2) + addShape(polygon, false, isLineStyle, isFillStyle2, isFillStyle1, isMove, &firstPoint, + currFillStyle1, currFillStyle2, currLineStyle); + } + + for (i = 0; i < (int)poly.size(); i++) { + if (i > 0) { + TPoint dp = toTwips(poly[i]->getP0()) - toTwips(poly[i - 1]->getP2()); + if (dp.x != 0 || dp.y != 0) { + addEdgeStraightToShape(polygon, dp); + currP.x += dp.x; + currP.y += dp.y; + } + } + + if (!it->m_isPoint) { + TPoint dp1 = toTwips(poly[i]->getP1()) - toTwips(poly[i]->getP0()); + TPoint dp2 = toTwips(poly[i]->getP2()) - toTwips(poly[i]->getP1()); + if (dp1 == dp2) + addEdgeStraightToShape(polygon, dp1 + dp2); + else if (dp1 == TPoint(0, 0)) + addEdgeStraightToShape(polygon, dp2); + else if (dp2 == TPoint(0, 0)) + addEdgeStraightToShape(polygon, dp1); + else + polygon->AddShapeRec(new FShapeRecEdgeCurved(dp1.x, dp1.y, dp2.x, dp2.y)); + + currP.x += dp1.x + dp2.x; + currP.y += dp1.y + dp2.y; + //m_of<<"CURRPOINT: ("<m_lineStyle.m_thickness, box); + currLineStyle = 0; + } + + updateBBox(box, currP); + } + + if (poly.size() != 1 && currP != lastPoint) { + addEdgeStraightToShape(polygon, lastPoint - currP); + currP.x += lastPoint.x - currP.x; + currP.y += lastPoint.y - currP.y; + //m_of<<"AGGIUNTO RACCORDO!CURRPOINT: ("<AddShapeRec(new FShapeRecEnd()); + + box = box.enlarge((int)(m_thickness * m_tw) + 1000); + + polygon->setBounds(new FRect(box.x0, box.y0, box.x1, box.y1)); + m_tags.AddFObj(polygon); + //m_of<< "aggiungo poly " <ID()<< " a depth "< 0, // ~ _hasClipDepth + false, true, false, + m_currDepth, polygonID, + affine2Matrix(m_affine), 0, 0, 0, + (clippedShapes > 0) ? m_currDepth + clippedShapes : 0 /**/); + + m_currDepth++; + m_currSprite->AddFObj(placePolygon); +} + +//------------------------------------------------------------------- +#ifdef LEVO +void TFlash::Imp::addCameraClip(int index) +{ + m_notClipped = false; + FRect *clipRectBounds = new FRect(0, 0, m_lx * m_tw, m_ly * m_tw); //coordinate values are in TWIPS + + FDTDefineShape3 *clipRectangle = new FDTDefineShape3(clipRectBounds); + + FColor black = FColor(0, 0, 0); + + U32 blackfillID = clipRectangle->AddSolidFillStyle(new FColor(black)); + clipRectangle->FinishStyleArrays(); + addShape(clipRectangle, false, true, true, false, true, 0, 0, blackfillID, 0); + + addEdgeStraightToShape(clipRectangle, 0, m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, m_lx * m_tw, 0); + addEdgeStraightToShape(clipRectangle, 0, -m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, -m_lx * m_tw, 0); + clipRectangle->AddShapeRec(new FShapeRecEnd()); + + // la depth e' 1 perche' riservata per la camera + m_tags.InsertFObj(index, new FCTPlaceObject2(true, false, true, false, 1, + clipRectangle->ID(), 0, 0, 0, 0, (U16)60000 /**/)); + m_tags.InsertFObj(index, clipRectangle); +} +#endif +//------------------------------------------------------------------- + +void computeQuadChain(const TEdge &e, + vector &quadArray, vector &toBeDeleted) +{ + int chunk_b, chunk_e, chunk = -1; + double t_b, t_e, w0, w1; + TThickQuadratic *q_b = 0, *q_e = 0; + TThickQuadratic dummy; + bool reversed = false; + + if (e.m_w0 > e.m_w1) { + reversed = true; + w0 = e.m_w1; + w1 = e.m_w0; + } else { + w0 = e.m_w0; + w1 = e.m_w1; + } + + if (w0 == 0.0) + chunk_b = 0; + else { + if (e.m_s->getChunkAndT(w0, chunk, t_b)) + assert(false); + q_b = new TThickQuadratic(); + toBeDeleted.push_back(q_b); + e.m_s->getChunk(chunk)->split(t_b, dummy, *q_b); + chunk_b = chunk + 1; + } + + if (w1 == 1.0) + chunk_e = e.m_s->getChunkCount() - 1; + else { + if (e.m_s->getChunkAndT(w1, chunk_e, t_e)) + assert(false); + q_e = new TThickQuadratic(); + toBeDeleted.push_back(q_e); + if (chunk_e == chunk) { + if (q_b) { + t_e = q_b->getT(e.m_s->getChunk(chunk)->getPoint(t_e)); + q_b->split(t_e, *q_e, dummy); + } else + e.m_s->getChunk(0)->split(t_e, *q_e, dummy); + + if (!reversed) + quadArray.push_back(q_e); + else { + quadArray.push_back(new TQuadratic(q_e->getP2(), q_e->getP1(), q_e->getP0())); + toBeDeleted.push_back(quadArray.back()); + } + return; + } + e.m_s->getChunk(chunk_e)->split(t_e, *q_e, dummy); + chunk_e--; + } + + int i; + assert(chunk_e >= chunk_b - 1); + + if (reversed) { + if (q_e) { + q_e->reverse(); + quadArray.push_back(q_e); + } + for (i = chunk_e; i >= chunk_b; i--) { + const TThickQuadratic *qAux = e.m_s->getChunk(i); + quadArray.push_back(new TQuadratic(qAux->getP2(), qAux->getP1(), qAux->getP0())); + toBeDeleted.push_back(quadArray.back()); + } + + if (q_b) { + q_b->reverse(); + quadArray.push_back(q_b); + } + } else { + if (q_b) + quadArray.push_back(q_b); + for (i = chunk_b; i <= chunk_e; i++) + quadArray.push_back((TQuadratic *)e.m_s->getChunk(i)); + if (q_e) + quadArray.push_back(q_e); + } +} + +//------------------------------------------------------------------- + +namespace +{ +inline bool isOuterEdge(const FlashPolyline &p) +{ + bool isTrasp1 = p.m_fillStyle1.m_type == None || (p.m_fillStyle1.m_type == Solid && p.m_fillStyle1.m_color1.m == 0); + bool isTrasp2 = p.m_fillStyle2.m_type == None || (p.m_fillStyle2.m_type == Solid && p.m_fillStyle2.m_color1.m == 0); + return (isTrasp1 ^ isTrasp2); +} +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addAutoclose(biPoint &bp, int edgeIndex) +{ + std::map::iterator it = m_autocloseMap.end(); + + bp.revert(); + it = m_autocloseMap.find(bp); + bp.revert(); + if (it != m_autocloseMap.end()) + (*it).second->m_fillStyle2 = m_polyData; + else if ((it = m_autocloseMap.find(bp)) != m_autocloseMap.end()) + (*it).second->m_fillStyle1 = m_polyData; + else { + FlashPolyline quadArray1; + quadArray1.m_depth = m_regionDepth * m_strokeCount + edgeIndex + 1; + quadArray1.m_fillStyle1 = m_polyData; + quadArray1.m_quads.push_back(new TQuadratic(bp.p0, .5 * (bp.p0 + bp.p1), bp.p1)); + m_quadsToBeDeleted.push_back(quadArray1.m_quads.back()); + + m_polylinesArray.push_back(quadArray1); + m_autocloseMap[bp] = &m_polylinesArray.back(); + } + + if (m_isMask && it != m_autocloseMap.end()) + (*it).second->m_skip = !isOuterEdge(*(*it).second); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addNewEdge(const TEdge &e) +{ + m_polylinesArray.push_back(FlashPolyline()); + FlashPolyline &quadArray = m_polylinesArray.back(); + m_edgeMap[e] = &m_polylinesArray.back(); + + computeQuadChain(e, quadArray.m_quads, m_quadsToBeDeleted); + + quadArray.m_fillStyle1 = m_polyData; + quadArray.m_depth = m_regionDepth * m_strokeCount + e.m_index + 1; + + if (e.m_s->getAverageThickness() > 0) { + assert(m_currPalette); + + TStrokeProp *prop = e.m_s->getProp(); + /////questo codice stava dentro tstroke::getprop///////// + TColorStyle *style = m_currPalette->getStyle(e.m_s->getStyle()); + if (!style->isStrokeStyle() || style->isEnabled() == false) + prop = 0; + else { + if (!prop || style != prop->getColorStyle()) { + e.m_s->setProp(style->makeStrokeProp(e.m_s)); + prop = e.m_s->getProp(); + } + } + + /////////// + if (prop) { + OutlineStrokeProp *aux = dynamic_cast(prop); + + if (aux) { + const TSolidColorStyle *st = dynamic_cast(aux->getColorStyle()); + if (st) { + quadArray.m_lineStyle.m_color1 = st->getMainColor(); + quadArray.m_lineStyle.m_type = Centerline; + quadArray.m_lineStyle.m_thickness = e.m_s->getAverageThickness(); + } + } + } + //quadArray.m_lineStyle.m_color1 = e.m_s->getStyle(); + } + if (quadArray.m_fillStyle1.m_type == None) { + //assert(clippedShapes>0); + quadArray.m_fillStyle1.m_type = Solid; + quadArray.m_fillStyle1.m_color1 = TPixel::Black; + } +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addEdge(const TEdge &e, TPointD &pBegin, TPointD &pEnd) +{ + TPointD auxP = pEnd; + + TEdge aux = e; + tswap(aux.m_w0, aux.m_w1); + std::map::iterator it; + //PolyStyle style = m_polyData; + std::stack auxs; + + if ((it = m_edgeMap.find(aux)) != m_edgeMap.end()) { + (*it).second->m_fillStyle2 = m_polyData; + + pBegin = (*it).second->m_quads.back()->getP2(); + pEnd = (*it).second->m_quads.front()->getP0(); + } else if ((it = m_edgeMap.find(e)) != m_edgeMap.end()) { + (*it).second->m_fillStyle1 = m_polyData; + + pBegin = (*it).second->m_quads.front()->getP0(); + pEnd = (*it).second->m_quads.back()->getP2(); + } else { + addNewEdge(e); + pBegin = m_polylinesArray.back().m_quads[0]->getP0(); + pEnd = m_polylinesArray.back().m_quads.back()->getP2(); + } + + if (m_isMask && it != m_edgeMap.end()) //per fare le maschere, non metto gli edge che hanno entrambe le sponde non trasparenti + (*it).second->m_skip = !isOuterEdge(*(*it).second); + + if (!areTwEqual(auxP, pBegin)) { + biPoint tmp(auxP, pBegin); + addAutoclose(tmp, e.m_index); + } + + if (m_properties.m_lineQuality.getValue() == ConstantLines) { + std::map>::iterator it; + + it = m_strokeMap.find(e.m_s); + if (it != m_strokeMap.end()) { + std::set::iterator it1; + wChunk wc(tmin(e.m_w0, e.m_w1), tmax(e.m_w0, e.m_w1)); + + it1 = it->second.find(wc); + if (it1 == it->second.end()) + it->second.insert(wc); + } else { + std::set chunkSet; + chunkSet.insert(wChunk(tmin(e.m_w0, e.m_w1), tmax(e.m_w0, e.m_w1))); + m_strokeMap[e.m_s] = chunkSet; + } + } +} + +//------------------------------------------------------------------- + +void TFlash::Imp::drawSubregions(TFlash *tf, const TRegion *r, const TPalette *palette) +{ + int i; + + //m_currentBgStyle.push_back(m_polyData); + m_regionDepth++; + + for (i = 0; i < (int)r->getSubregionCount(); i++) { + TRegion *region = r->getSubregion(i); + TRegionProp *prop = region->getProp(/*palette*/); + ////prima questo codice stava dentro tregion::getprop//// + int styleId = region->getStyle(); + //if (styleId) + { + TColorStyle *style = palette->getStyle(styleId); + if (!style->isRegionStyle() || style->isEnabled() == false) + prop = 0; + else if (!prop || style != prop->getColorStyle()) { + region->setProp(style->makeRegionProp(region)); + prop = region->getProp(); + } + + //////// + if (prop) + prop->draw(*tf); + + //m_currentBgStyle.push_back(m_polyData); + drawSubregions(tf, region, palette); + //m_currentBgStyle.pop_back(); + } + } + m_regionDepth--; + + //m_currentBgStyle.pop_back(); +} +//------------------------------------------------------------------- + +bool comparevector(const FlashPolyline &p1, const FlashPolyline &p2) +{ + return p2.m_depth > p1.m_depth; +} + +//------------------------------------------------------------------- + +void TFlash::Imp::buildRegion(TFlash *tf, const TVectorImageP &vi, int regionIndex) +{ + TRegion *region = vi->getRegion(regionIndex); + TRectD rect = region->getBBox(); + double lx = rect.x1 - rect.x0; + double ly = rect.y1 - rect.y0; + if (lx < 0.5 || ly < 0.5) + return; + + TRegionProp *prop = region->getProp(); + + int styleId = region->getStyle(); + + TColorStyle *style = vi->getPalette()->getStyle(styleId); + + if (!style->isRegionStyle() || style->isEnabled() == false) + prop = 0; + else if (!prop || style != prop->getColorStyle()) { + region->setProp(style->makeRegionProp(region)); + prop = region->getProp(); + } + + tf->setThickness(0); + if (prop) + prop->draw(*tf); + + drawSubregions(tf, region, m_currPalette); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::buildStroke(TFlash *tf, const TVectorImageP &vi, int strokeIndex) +{ + TStroke *stroke = vi->getStroke(strokeIndex); + if (stroke->getBBox().isEmpty()) + return; + + TStrokeProp *prop = stroke->getProp(); + /////questo codice stava dentro tstroke::getprop///////// + TColorStyle *style = vi->getPalette()->getStyle(stroke->getStyle() /*m_imp->m_styleId*/); + if (!style->isStrokeStyle() || style->isEnabled() == false) + prop = 0; + else if (!prop || style != prop->getColorStyle()) { + stroke->setProp(style->makeStrokeProp(stroke)); + prop = stroke->getProp(); + } + + if (prop) + prop->draw(*tf); +} + +//------------------------------------------------------------------- + +USHORT TFlash::Imp::buildVectorImage(const TVectorImageP &_vi, TFlash *tf, double &scaleFactor, bool isMask) +{ + USHORT id; + int i; + const TPalette *oldPalette; + + assert(m_regionDepth == 0); + + m_strokeCount = _vi->getStrokeCount(); + + std::vector strokes; + for (i = 0; i < m_strokeCount; i++) + strokes.push_back(i); + + _vi->enableMinimizeEdges(false); + _vi->notifyChangedStrokes(strokes, vector()); + _vi->enableMinimizeEdges(true); + + TRectD box = _vi->getBBox(); + int dim = tmax(convert(box).getLx(), convert(box).getLy()); + + TVectorImageP vi; + if (dim * m_tw > 32767.0) { + scaleFactor = dim * m_tw / 32767.0; + vi = _vi->clone(); + vi->transform(TScale(1.0 / scaleFactor), true); + } else + vi = _vi; + + oldPalette = m_currPalette; + m_currPalette = vi->getPalette(); + + bool newSprite = false; + + if (m_currSprite == 0) //non e' un image pattern!!! + { + m_currSprite = new FDTSprite(); + m_currDepth = 1; + newSprite = true; + } + /*assert(m_currSprite==0); +m_currSprite = new FDTSprite(); +m_currDepth = 1;*/ + + tf->setThickness(0); + m_isMask = isMask; + if (!isMask) + for (i = 0; i < (int)vi->getStrokeCount(); i++) + vi->getStroke(i)->setAverageThickness((m_properties.m_lineQuality.getValue() == ConstantLines) ? computeAverageThickness(vi->getStroke(i)) : 0); + + UINT strokeIndex = 0; + int parentDepth = 0; + while (strokeIndex < vi->getStrokeCount()) // ogni ciclo di while fa un gruppo + { + FDTSprite *parentSprite = 0; + int currStrokeIndex = strokeIndex; + if (vi->isStrokeGrouped(currStrokeIndex) != 0) { + parentSprite = m_currSprite; + parentDepth = m_currDepth; + m_currSprite = new FDTSprite(); + m_currDepth = 0; + } + for (UINT regionIndex = 0; regionIndex < vi->getRegionCount(); regionIndex++) + if (vi->sameGroupStrokeAndRegion(currStrokeIndex, regionIndex)) + buildRegion(tf, vi, regionIndex); + + if (m_properties.m_lineQuality.getValue() != ConstantLines) + tf->drawHangedObjects(); + //else + m_polylinesArray.sort(comparevector); + + while (strokeIndex < vi->getStrokeCount() && vi->sameGroup(strokeIndex, currStrokeIndex)) { + if (!isMask) + buildStroke(tf, vi, strokeIndex); + strokeIndex++; + } + + if (isMask && vi->getRegionCount() == 0) //pezza:se non ci sono regioni, la maschera + //deve mostrare il nulla; per fare cio', metto + //un poligono posticcio(non mascherante, puro stroke + //centerline) nella sprite, altrimenti + //invece di non vedersi nulla si vede l' + //immagine mascherata completa.. + { + FlashPolyline quads; + TQuadratic qaux(TPointD(0, 0), TPointD(10, 10), TPointD(20, 20)); + quads.m_quads.push_back(&qaux); + m_polylinesArray.push_back(quads); + } + tf->drawHangedObjects(); + if (parentSprite) { + m_tags.AddFObj(m_currSprite); + parentSprite->AddFObj(new FCTPlaceObject2(false, // hasClipDepth + false, // hasRatio, + true, // hasCharId, + false, // hasMove, + parentDepth++, //depth + m_currSprite->ID(), //id + affine2Matrix(TAffine()), //matrix + 0, 0, 0, 0)); + + m_currSprite = parentSprite; + m_currDepth = parentDepth; + parentSprite = 0; + } + } + + id = m_currSprite->ID(); + + if (newSprite) { + m_currSprite->AddFObj(new FCTShowFrame()); + m_tags.AddFObj(m_currSprite); + m_currSprite = 0; + } + m_currPalette = oldPalette; + + return id; +} + +//------------------------------------------------------------------- + +USHORT TFlash::Imp::buildRasterImage(const TImageP img, TFlash *tf) +{ + if (m_currSprite) //e' un custom style + { + std::map::iterator it; + if (m_keepImages && (it = m_imagesMap.find(img.getPointer())) != m_imagesMap.end()) { + m_currSprite->AddFObj(new FCTPlaceObject2(false, // hasClipDepth + false, // hasRatio, + true, // hasCharId, + false, //i!=0, // hasMove, + m_currDepth++, it->second, affine2Matrix(m_affine), + 0, 0, 0, 0)); + + return 0; + } + } + + TToonzImageP tim = (TToonzImageP)img; + TRasterImageP rim = (TRasterImageP)img; + TRasterP ri; + + if (tim) { + TRaster32P raux(tim->getSize()); + TRop::convert(raux, tim->getRaster(), img->getPalette(), TRect(0, 0, tim->getSize().lx - 1, tim->getSize().ly - 1), false, true); + ri = raux; + } else if (!((TRaster32P)rim->getRaster())) { + TRaster32P raux(rim->getRaster()->getSize()); + TRop::convert(raux, rim->getRaster()); + ri = raux; + } else + ri = rim->getRaster(); + + //TImageWriter::save(TFilePath("C:\\temp\\flame.tif"), ri); + + int lx = ri->getLx(), ly = ri->getLy(), wrap = ri->getWrap(); + + /* +double dpix=72.,dpiy=72.; + +if (rim) + rim->getDpi(dpix,dpiy); +else + tim->getDpi(dpix,dpiy); + +if (dpix==0 && dpiy==0) +{ +dpix=cameradpix; +dpiy=cameradpiy; +} +*/ + + //const double factor = Stage::inch; + + //double unit = 100; + //int realLx = lx;//(int)(cameradpix * lx / dpix); + //int realLy = ly;//(int)(cameradpiy * ly / dpiy); + + std::vector *buffer = new std::vector(); + + //TRaster32P newRaster= TRop::copyAndSwapRBChannels(ri); + + //#ifdef TNZ_MACHINE_CHANNEL_ORDER_MRGB + + //TRaster32P newRaster= TRop::copyAndSwapRBChannels(ri); + //Tiio::createJpg(*buffer, newRaster, m_properties.m_jpgQuality.getValue()); + //#else + + Tiio::createJpg(*buffer, ri, m_properties.m_jpgQuality.getValue()); + m_toBeDeleted.push_back(buffer); + + //#endif + + int bitmapId; + if (m_supportAlpha) { + std::vector alphachannel(lx * ly); + + ri->lock(); + TPixel *auxin, *bufin = (TPixel *)ri->getRawData(); + + int i, j, count = 0; + for (i = 0; i < ly; i++) { + auxin = bufin + (ly - i - 1) * wrap; + for (j = 0; j < lx; j++, auxin++) + alphachannel[count++] = auxin->m; + } + ri->unlock(); + + U32 zippedAlphaSizeOut = (U32)(2 * ((alphachannel.size() * 1.1) + 12)); + std::vector *zippedAlphaChannel = new std::vector(zippedAlphaSizeOut); + compress(&(*zippedAlphaChannel)[0], (uLongf *)&zippedAlphaSizeOut, (const UCHAR *)&alphachannel[0], alphachannel.size()); + zippedAlphaChannel->resize(zippedAlphaSizeOut); + m_toBeDeleted.push_back(zippedAlphaChannel); + FDTDefineBitsJPEG3 *bitmap = new FDTDefineBitsJPEG3((U8 *)&(*buffer)[0], buffer->size(), &(*zippedAlphaChannel)[0], zippedAlphaSizeOut); + m_tags.AddFObj(bitmap); + bitmapId = bitmap->ID(); + } else { + FDTDefineBitsJPEG2 *bitmap = new FDTDefineBitsJPEG2((U8 *)&(*buffer)[0], buffer->size()); + m_tags.AddFObj(bitmap); + bitmapId = bitmap->ID(); + } + + // + //m_totMem += (buffer->size()/*+zippedAlphaChannel->size()*/)/1024.0; + + FRect *rectBounds = new FRect(-lx * m_sCoord1 / 2, -ly * m_sCoord1 / 2, lx * m_sCoord1 / 2, ly * m_sCoord1 / 2); + FDTDefineShape3 *rectangle = new FDTDefineShape3(rectBounds); + + FMatrix *matrix1 = new FMatrix(true, m_tw * Fixed1, m_tw * Fixed1, false, 0, 0, -(lx / 2) * m_sCoord1, -(ly / 2) * m_sCoord1); + FFillStyle *fill1 = new FFillStyleBitmap(false, bitmapId, matrix1); + + U32 fillStyle1_ID = rectangle->AddFillStyle(fill1); + + rectangle->FinishStyleArrays(); + addShape(rectangle, false, false, true, false, true, (lx / 2) * m_sCoord1, (ly / 2) * m_sCoord1, 0, + fillStyle1_ID, 0); + + addEdgeStraightToShape(rectangle, -lx * m_sCoord1, 0); + addEdgeStraightToShape(rectangle, 0, -ly * m_sCoord1); + addEdgeStraightToShape(rectangle, lx * m_sCoord1, 0); + addEdgeStraightToShape(rectangle, 0, ly * m_sCoord1); + rectangle->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(rectangle); + + if (m_currSprite) { + //e' un custom style + + m_imagesMap[img.getPointer()] = rectangle->ID(); + + m_currSprite->AddFObj(new FCTPlaceObject2(false, // hasClipDepth + false, // hasRatio, + true, // hasCharId, + false, //i!=0, // hasMove, + m_currDepth++, rectangle->ID(), affine2Matrix(m_affine), + 0, 0, 0, 0)); + } + + return rectangle->ID(); + //delete bufout; +} + +//------------------------------------------------------------------- + +USHORT TFlash::Imp::buildImage(const TImageP img, TFlash *tf, double &scaleFactor, bool isMask) +{ + scaleFactor = 1.0; + + if (img->getType() == TImage::VECTOR) + return buildVectorImage((TVectorImageP)img, tf, scaleFactor, isMask); + else + return buildRasterImage(img, tf); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addSoundToFrame(bool isLast) +{ + if (!m_sound.empty()) { + TSoundTrackP sound = *m_sound.begin(); + if ((int)m_sound.size() == m_soundSize) { + bool is16Bit = (sound->getBitPerSample() == 16); + bool isStereo = (sound->getChannelCount() == 2); + UCHAR rate; + switch (sound->getSampleRate() / 5000) { + case 1: // 5k + rate = 0; + break; + case 2: // 11k + rate = 1; + break; + case 4: // 22k + rate = 2; + break; + case 8: // 44k + rate = 3; + break; + default: + rate = 0; + } + UCHAR format = 4 * (rate) + is16Bit * 2 + isStereo; + FDTSoundStreamHead2 *head = new FDTSoundStreamHead2( + format, c_soundCompression, rate, is16Bit, isStereo, (USHORT)sound->getSampleCount()); + m_tags.AddFObj(head); + } + int bytes = sound->getSampleCount() * sound->getSampleSize(); + U8 *aux = new U8[bytes]; + +#if TNZ_LITTLE_ENDIAN + memcpy(aux, sound->getRawData(), bytes); +#else //su mac, gli short vanno girati!! + int ii; + assert(c_soundBps == 16); + const U8 *buf = sound->getRawData(); + for (ii = 0; ii < (bytes & (~0x1)); ii += 2) + aux[ii] = buf[ii + 1], aux[ii + 1] = buf[ii]; +#endif + m_tags.AddFObj(new FDTSoundStreamBlock(bytes, aux)); + + m_soundBuffer.push_back(aux); + m_sound.erase(m_sound.begin()); + if (isLast) + m_sound.erase(m_sound.begin(), m_sound.end()); + } +} + +//------------------------------------------------------------------- +void TFlash::setGlobalScale(const TAffine &aff) +{ + m_imp->m_globalScale = aff; +} + +//------------------------------------------------------------------- + +void TFlash::Imp::writeFrame(TFlash *tf, bool isLast, int frameCountLoader, bool lastScene) +{ + if (!m_frameData) + return; + int oldSize = m_oldFrameData ? (int)m_oldFrameData->size() : -1; + int depth; + //bool firstTime = true; + + static bool loaderAdded = false; + bool insiderLoader = (frameCountLoader == 1); + int currTagIndex = m_tags.GetFObjCount() - 1; + //bool putCameraClip = false; + //aggiungo l'istruzione per evitare preloader se e' gia + //caricato + if (m_currFrameIndex == 1) { + if (m_properties.m_preloader.getValue() && frameCountLoader >= 1) + addSkipLoader(frameCountLoader + 1); + + if (m_properties.m_url.getValue().length() > 0) + addUrlLink(toString(m_properties.m_url.getValue())); + } + + if (m_currFrameIndex > m_soundOffset) + addSoundToFrame(isLast); + + double scaleFactor; + + std::map::iterator it; + bool lastOneIsNotMasked = true; + TImageP currMask = 0; + int currMaskDepth; + bool animatedPalette = false; + + for (depth = 0; depth < tmax((int)m_frameData->size(), oldSize); depth++) { + + FCXFormWAlpha *form = 0; + + if (depth < (int)m_frameData->size()) { + TImageP img = (*m_frameData)[depth].m_img; + + if ((*m_frameData)[depth].m_isMask) { + currMask = img; + currMaskDepth = depth; + lastOneIsNotMasked = true; + continue; + } else if ((*m_frameData)[depth].m_isMasked && lastOneIsNotMasked) { + //assert(currMask); + lastOneIsNotMasked = false; + int numMasked = 1; + while (depth + numMasked < (int)m_frameData->size() && (*m_frameData)[depth + numMasked].m_isMasked) + numMasked++; + if (m_oldFrameData && currMaskDepth < (int)m_oldFrameData->size()) + m_tags.AddFObj(new FCTRemoveObject2(currMaskDepth + 3)); + if (currMaskDepth < (int)m_frameData->size() && currMask) //&& + //(currMask->getType()!=TImage::VECTOR || ((TVectorImage*)currMask)->getRegionCount()>0)) + { + UINT id = buildImage(currMask, tf, scaleFactor, true); + assert(scaleFactor >= 1.0); + TAffine aff = TTranslation(0.5 * m_lx, -0.5 * m_ly) * m_globalScale * TScale(scaleFactor); + m_tags.AddFObj(new FCTPlaceObject2(true, // hasClipDepth + false, // hasRatio, + true, // hasCharId, + false, //i!=0, // hasMove, + currMaskDepth + 3, id, affine2Matrix(aff), + 0, 0, 0, currMaskDepth + 3 + numMasked)); + } + + } else if (!(*m_frameData)[depth].m_isMasked) + lastOneIsNotMasked = true; + TVectorImageP vimg = (TVectorImageP)img; + if (vimg) { + assert(vimg->getPalette()); + animatedPalette = vimg->getPalette()->isAnimated(); + } + } + + if (m_oldFrameData && depth < (int)m_oldFrameData->size() && depth < (int)m_frameData->size() && + (*m_frameData)[depth].m_img.getPointer() == (*m_oldFrameData)[depth].m_img.getPointer() && !animatedPalette) { + TImageP img = (*m_frameData)[depth].m_img; + const TColorFunction *ct = (*m_frameData)[depth].m_cf; + TColorFunction::Parameters p; + if (ct && ct->getParameters(p)) + form = new FCXFormWAlpha(1, 1, (TINT32)(p.m_mR * 256), (TINT32)(p.m_mG * 256), (TINT32)(p.m_mB * 256), (TINT32)(p.m_mM * 256), + (TINT32)(p.m_cR), (TINT32)(p.m_cG), (TINT32)(p.m_cB), (TINT32)(p.m_cM)); + + if ((*m_frameData)[depth].m_aff != (*m_oldFrameData)[depth].m_aff) { + std::map::iterator it1; + it1 = m_imagesScaleMap.find(img.getPointer()); + assert(it1 != m_imagesScaleMap.end()); + scaleFactor = (*it1).second; + + TAffine aff = TTranslation(0.5 * m_lx, -0.5 * m_ly) * m_globalScale * (*m_frameData)[depth].m_aff * TScale(scaleFactor); + /*if (m_notClipped && !putCameraClip) + { + TRectD box = aff*img->getBBox(); + if (box.x0<0 || -box.y1<0 || box.x1>m_lx || -box.y0>m_ly) + putCameraClip = true; + }*/ + m_tags.AddFObj(new FCTPlaceObject2(false, // hasClipDepth + false, // hasRatio, + false, // hasCharId, + true, //i!=0, // hasMove, + depth + 3, 0, affine2Matrix(aff), + form, 0, 0, 0)); + } + } else { + if (m_oldFrameData && depth < (int)m_oldFrameData->size()) + m_tags.AddFObj(new FCTRemoveObject2(depth + 3)); + if (depth < (int)m_frameData->size()) { + TImageP img = (*m_frameData)[depth].m_img; + const TColorFunction *cf = (*m_frameData)[depth].m_cf; + TColorFunction::Parameters p; + if (cf && cf->getParameters(p)) + form = new FCXFormWAlpha(1, 1, (TINT32)(p.m_mR * 256), (TINT32)(p.m_mG * 256), (TINT32)(p.m_mB * 256), (TINT32)(p.m_mM * 256), + (TINT32)(p.m_cR), (TINT32)(p.m_cG), (TINT32)(p.m_cB), (TINT32)(p.m_cM)); + it = m_keepImages ? m_imagesMap.find(img.getPointer()) : m_imagesMap.end(); + USHORT id; + if (it == m_imagesMap.end()) { + id = buildImage(img, tf, scaleFactor, false); + if (!animatedPalette) { + m_imagesMap[img.getPointer()] = id; + m_imagesScaleMap[img.getPointer()] = scaleFactor; + } + } else { + id = (*it).second; + std::map::iterator it1; + it1 = m_imagesScaleMap.find(img.getPointer()); + assert(it1 != m_imagesScaleMap.end()); + scaleFactor = (*it1).second; + } + assert(scaleFactor >= 1.0); + TAffine aff = TTranslation(0.5 * m_lx, -0.5 * m_ly) * m_globalScale * (*m_frameData)[depth].m_aff * TScale(scaleFactor); + /*if (m_notClipped && !putCameraClip) + { + TRectD box = img->getBBox(); + + box = aff*box; + + if (box.x0<0 || -box.y1<0 || box.x1>m_lx || -box.y0>m_ly) + putCameraClip = true; + }*/ + + /*Commento al seguente if: + questa cosa e' davvero FOLLE. C'era un baco per cui se facevi rewind + nel flash player mentre era in play restavano appese alcune immagini. + Dopo indagini e confronti con gli swf generati da flash mx, ho scoperto che , quando si fa place di una sprite + ad una certa depth, sostituendo un'altra sprite alla stessa depth, mx mette hasRatio=true con un valore + di ratio pari a 0x8!!!! Non capisco, ma lo faccio anch'io e funziona, don't ask me why. Vincenzo. */ + + int ratioVal = 0; + if (m_oldFrameData && (int)m_oldFrameData->size() > depth && ((*m_oldFrameData)[depth].m_img)) + ratioVal = 0x8; + + m_tags.AddFObj(new FCTPlaceObject2(false, // hasClipDepth + ratioVal > 0, // hasRatio, + true, // hasCharId, + false, //i!=0, // hasMove, + depth + 3, id, affine2Matrix(aff), + form, ratioVal, 0, 0)); + } + } + } + + //inserisce lo stop sull'ultimo frame + if (isLast && lastScene && !m_properties.m_looping.getValue()) { + addActionStop(); + //loaderAdded = false; + } + + m_tags.AddFObj(new FCTShowFrame()); + + //if (putCameraClip) + //addCameraClip(currTagIndex); + + //ho una sola scena => loader interno I frame oppure + //ci sono piu' scene => la I fa da loader + if (m_properties.m_preloader.getValue() && !m_loaderAdded && (insiderLoader || (isLast && frameCountLoader > 1))) { + for (depth = 0; depth < (int)m_frameData->size(); depth++) + m_tags.AddFObj(new FCTRemoveObject2(depth + 3)); + if (m_oldFrameData) + delete m_oldFrameData; + m_oldFrameData = m_frameData; + m_frameData = 0; + addLoader(); + m_tags.AddFObj(new FCTShowFrame()); + m_currFrameIndex++; + //inserisce lo stop al primo frame che segue il loader + if (!m_properties.m_autoplay.getValue() && loaderAdded && m_currFrameIndex == frameCountLoader + 1) + addPause(); + //m_properties.m_preloader = false; + m_loaderAdded = true; + } + //else if (!m_properties.m_autoplay.getValue()) + // addPauseAtStart(); + + if (m_frameData && isLast && m_currFrameIndex != 1) + for (depth = 0; depth < (int)m_frameData->size(); depth++) + m_tags.AddFObj(new FCTRemoveObject2(depth + 3)); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addActionStop() +{ + //construct an empty CTDoAction tag object + FCTDoAction *doAction = new FCTDoAction(); + + //add the stop action to the CTDoAction tag object + doAction->AddAction(new FActionStop()); + + //add the CTDoAction object to allTags + m_tags.AddFObj(doAction); +} + +//------------------------------------------------------------------- +void TFlash::Imp::addLoader() +{ + string target = ""; + //construct an empty CTDoAction tag object + FCTDoAction *doAction = new FCTDoAction(); + + doAction->AddAction(new FActionGotoFrame((U16)0)); + doAction->AddAction(new FActionPlay()); + + //add the CTDoAction object to allTags + m_tags.AddFObj(doAction); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addSkipLoader(int jumpToFrame) +{ + string target = ""; + //construct an empty CTDoAction tag object + FCTDoAction *doAction = new FCTDoAction(); + + //controlla se il file e' stato tutto caricato + doAction->AddAction(new FActionPush(new FString((U8 *)target.c_str()))); + doAction->AddAction(new FActionPush(7, FLOAT(12))); + doAction->AddAction(new FActionGetProperty()); + + doAction->AddAction(new FActionPush(new FString((U8 *)target.c_str()))); + doAction->AddAction(new FActionPush(7, FLOAT(5))); + doAction->AddAction(new FActionGetProperty()); + + doAction->AddAction(new FActionEquals2()); + doAction->AddAction(new FActionNot()); + doAction->AddAction(new FActionIf(5 + 1)); + doAction->AddAction(new FActionGotoFrame((U16)jumpToFrame)); + doAction->AddAction(new FActionPlay()); + + //add the CTDoAction object to allTags + m_tags.AddFObj(doAction); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addPause() +{ + addActionStop(); + + FRect *clipRectBounds = new FRect(0, 0, m_lx * m_tw, m_ly * m_tw); //coordinate values are in TWIPS + + FDTDefineShape3 *clipRectangle = new FDTDefineShape3(clipRectBounds); + + FColor black = FColor(0, 0, 0); + + U32 blackfillID = clipRectangle->AddSolidFillStyle(new FColor(black)); + clipRectangle->FinishStyleArrays(); + addShape(clipRectangle, false, true, true, false, true, 0, 0, blackfillID, 0); + + addEdgeStraightToShape(clipRectangle, 0, m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, m_lx * m_tw, 0); + addEdgeStraightToShape(clipRectangle, 0, -m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, -m_lx * m_tw, 0); + clipRectangle->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(clipRectangle); + + FDTDefineButton2 *theButton = new FDTDefineButton2(0); //flag is 0 to indicate a push button + FMatrix *mx = new FMatrix(); + FCXFormWAlpha *cxf = new FCXFormWAlpha(false, false, 0, 0, 0, 0, 0, 0, 0, 0); + FButtonRecord2 *bRec = new FButtonRecord2(true, false, false, false, clipRectangle->ID(), 1, mx, cxf); + theButton->AddButtonRecord(bRec); + + //the button action + FActionCondition *ac = new FActionCondition(); + ac->AddCondition(OverDownToOverUp); + ac->AddActionRecord(new FActionPlay()); + theButton->AddActionCondition(ac); + + m_tags.AddFObj(theButton); + + FMatrix *matrix = new FMatrix(); + // la depth e' 2 perche' riservata per il bottone + FCTPlaceObject2 *placeButton = new FCTPlaceObject2(false, // ~ _hasClipDepth + true, true, false, + 2, theButton->ID(), matrix, 0, 0, 0, 0); + m_tags.AddFObj(placeButton); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::addUrlLink(const string _url) +{ + //addActionStop(); + + string url; + if (url.find("http") == -1) + url = "http://" + _url; + else + url = _url; + + FRect *clipRectBounds = new FRect(0, 0, m_lx * m_tw, m_ly * m_tw); //coordinate values are in TWIPS + + FDTDefineShape3 *clipRectangle = new FDTDefineShape3(clipRectBounds); + + FColor black = FColor(0, 0, 0, 0); + + U32 blackfillID = clipRectangle->AddSolidFillStyle(new FColor(black)); + clipRectangle->FinishStyleArrays(); + addShape(clipRectangle, false, true, true, false, true, 0, 0, blackfillID, 0); + + addEdgeStraightToShape(clipRectangle, 0, m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, m_lx * m_tw, 0); + addEdgeStraightToShape(clipRectangle, 0, -m_ly * m_tw); + addEdgeStraightToShape(clipRectangle, -m_lx * m_tw, 0); + clipRectangle->AddShapeRec(new FShapeRecEnd()); + + m_tags.AddFObj(clipRectangle); + + FDTDefineButton2 *theButton = new FDTDefineButton2(0); //flag is 0 to indicate a push button + FMatrix *mx = new FMatrix(); + FCXFormWAlpha *cxf = new FCXFormWAlpha(false, false, 0, 0, 0, 0, 0, 0, 0, 0); + FButtonRecord2 *bRec = new FButtonRecord2(true, false, false, false, clipRectangle->ID(), 1, mx, cxf); + theButton->AddButtonRecord(bRec); + + //the button action + FActionCondition *ac = new FActionCondition(); + ac->AddCondition(OverDownToOverUp); + ac->AddActionRecord(new FActionGetURL(new FString(url.c_str()), new FString(""))); + theButton->AddActionCondition(ac); + + m_tags.AddFObj(theButton); + + FMatrix *matrix = new FMatrix(); + // la depth e' 2 perche' riservata per il bottone + FCTPlaceObject2 *placeButton = new FCTPlaceObject2(false, // ~ _hasClipDepth + true, true, false, + 2, theButton->ID(), matrix, 0, 0, 0, 0); + m_tags.AddFObj(placeButton); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::beginMask() +{ + //m_currMask = TVectorImageP(); + + m_currMask = new TVectorImage(); + m_currMask->setPalette(new TPalette()); +} + +//------------------------------------------------------------------- + +void TFlash::Imp::endMask() +{ + m_frameData->push_back(FlashImageData(TAffine(), m_currMask, 0, true, false)); + m_currMask = TVectorImageP(); +} + +//------------------------------------------------------------------- + +bool TFlash::Imp::drawOutline(TStroke *s, bool separeDifferentColors) +{ + if (m_polyData.m_color1 != m_currStrokeColor) { + //bool inserted = m_outlineColors.insert(m_polyData.m_color1).second; + if (!m_outlines.empty()) { + computeOutlineBoundary(m_outlines, m_polylinesArray, m_currStrokeColor); + } + if (!m_polylinesArray.empty() && (separeDifferentColors /* || !inserted*/)) //non posso accorpare strokes di colore diverso....flash macromedia non le importa bene! lo stronzo. + drawHangedObjects(); + m_currStrokeColor = m_polyData.m_color1; + } + m_outlines.push_back(s); + return true; +} + +//=================================================================== +TFlash::TFlash(int lx, int ly, int frameCount, int frameRate, TPropertyGroup *properties, bool keepImages) + : m_imp(new Imp(lx, ly, frameCount, frameRate, properties, keepImages)) +{ +} + +//------------------------------------------------------------------- + +TFlash::~TFlash() +{ + delete m_imp; +} + +//------------------------------------------------------------------- + +void TFlash::setBackgroundColor(const TPixel32 &bgColor) +{ + FCTSetBackgroundColor *background = + new FCTSetBackgroundColor(new FColor(bgColor.r, bgColor.g, bgColor.b)); + m_imp->m_tags.AddFObj(background); +} + +//------------------------------------------------------------------- + +void TFlash::setThickness(double thickness) +{ + m_imp->m_thickness = thickness; +} + +//------------------------------------------------------------------- + +void TFlash::setFillColor(const TPixel32 &color) +{ + m_imp->m_polyData.m_color1 = color; + m_imp->m_polyData.m_type = Solid; +} + +//------------------------------------------------------------------- + +void TFlash::setLineColor(const TPixel32 &color) +{ + m_imp->m_lineColor = color; +} + +//------------------------------------------------------------------- + +void TFlash::setTexture(const TRaster32P &texture) +{ + m_imp->m_polyData.m_type = Texture; + m_imp->m_polyData.m_texture = texture; +} + +//------------------------------------------------------------------- + +void TFlash::setGradientFill(bool isLinear, const TPixel &color1, const TPixel &color2, double smooth) +{ + m_imp->m_polyData.m_type = (isLinear) ? LinearGradient : RadialGradient; + m_imp->m_polyData.m_color1 = color1; + m_imp->m_polyData.m_color2 = color2; + m_imp->m_polyData.m_smooth = smooth; +} + +//------------------------------------------------------------------- + +void TFlash::setFillStyleMatrix(const TAffine &aff) +{ + m_imp->m_polyData.m_matrix = aff; +} + +//------------------------------------------------------------------- + +void TFlash::drawSegments(const vector segmentArray, bool isGradientColor) +{ + m_imp->drawSegments(segmentArray, isGradientColor); +} + +//------------------------------------------------------------------- + +void TFlash::drawquads(const vector quadArray) +{ + m_imp->drawquads(quadArray); +} + +//------------------------------------------------------------------- + +void TFlash::cleanCachedImages() +{ + m_imp->m_imagesMap.clear(); + m_imp->m_imagesScaleMap.clear(); + m_imp->m_texturesMap.clear(); +} + +//=================================================================== + +void TFlash::drawCenterline(const TStroke *s, bool drawAll) +{ + //vector quads; + int i; + double thickness; + + if (m_imp->m_thickness > 0) + thickness = m_imp->m_thickness; + else if (m_imp->m_properties.m_lineQuality.getValue() != ConstantLines) + thickness = computeAverageThickness(s); + else + thickness = s->getAverageThickness(); + + if (m_imp->m_lineColor.m == 0 || thickness == 0) + return; + + if (m_imp->m_properties.m_lineQuality.getValue() != ConstantLines && m_imp->m_lineColor != m_imp->m_currStrokeColor) { + drawHangedObjects(); + //m_imp->m_outlineColors.insert(m_imp->m_lineColor).second; + m_imp->m_currStrokeColor = m_imp->m_lineColor; + } + + FlashPolyline quads; + if (s->getChunkCount() == 1 && + norm2(m_imp->toTwips(s->getChunk(0)->getP2()) - m_imp->toTwips(s->getChunk(0)->getP0())) <= 4 //metto lo stile di fill: i punti si disegnano come cerchi + && thickness > 0) { + quads.m_isPoint = true; + quads.m_fillStyle1.m_type = Solid; + quads.m_fillStyle1.m_color1 = m_imp->m_lineColor; + } + + quads.m_lineStyle.m_type = Centerline; + quads.m_lineStyle.m_thickness = thickness; + quads.m_lineStyle.m_color1 = m_imp->m_lineColor; + + //quads.m_lineStyle.m_isRegion = false; + //quads.m_lineStyle.m_isHole = false; + + std::map>::iterator it = m_imp->m_strokeMap.end(); + + if (!drawAll) + it = m_imp->m_strokeMap.find(s); + + if (it == m_imp->m_strokeMap.end()) { + for (i = 0; i < s->getChunkCount(); i++) + quads.m_quads.push_back((TQuadratic *)s->getChunk(i)); + m_imp->m_polylinesArray.push_back(quads); + } else { + std::set::iterator it1; + double oldW1 = 0; + for (it1 = it->second.begin(); it1 != it->second.end(); it1++) { + if (it1->w0 > oldW1) { + putquads(s, oldW1, it1->w0, quads.m_quads); + m_imp->m_polylinesArray.push_back(quads); + quads.m_quads.clear(); + } + oldW1 = it1->w1; + } + if (oldW1 < 1.0) { + putquads(s, oldW1, 1.0, quads.m_quads); + m_imp->m_polylinesArray.push_back(quads); + quads.m_quads.clear(); + } + } +} +void TFlash::drawHangedObjects() +{ + m_imp->drawHangedObjects(); +} +//------------------------------------------------------------------- + +int TFlash::drawRectangle(const TRectD &rect) +{ + m_imp->drawHangedObjects(); + + vector v; + + v.push_back(rect.getP00()); + v.push_back(rect.getP01()); + v.push_back(rect.getP11()); + v.push_back(rect.getP10()); + + return m_imp->drawPolyline(v); +} + +//------------------------------------------------------------------- + +int TFlash::drawPolyline(vector &poly) +{ + m_imp->drawHangedObjects(); + + return m_imp->drawPolyline(poly); +} + +//------------------------------------------------------------------- + +void TFlash::drawPolygon(vector> &quads, int clippedShapes) +{ + m_imp->drawHangedObjects(); + + //std::list::iterator it = polylines.begin(), it_e = polylines.end(); + list polylines; + + for (int i = 0; i < (int)quads.size(); i++) { + polylines.push_back(FlashPolyline()); + polylines.back().m_quads = quads[i]; + polylines.back().m_fillStyle1 = m_imp->m_polyData; + } + + m_imp->doDrawPolygon(polylines, clippedShapes); +} + +//------------------------------------------------------------------- +void TFlash::drawDot(const TPointD ¢er, double radius) +{ + m_imp->drawDot(center, radius); +} + +//------------------------------------------------------------------- + +int TFlash::drawEllipse(const TPointD ¢er, double radiusX, double radiusY) +{ + m_imp->drawHangedObjects(); + + return m_imp->drawEllipse(center, radiusX, radiusY); +} + +//------------------------------------------------------------------- + +void TFlash::pushMatrix() +{ + m_imp->m_matrixStack.push_back(m_imp->m_affine); +} + +//------------------------------------------------------------------- + +void TFlash::popMatrix() +{ + assert(!m_imp->m_matrixStack.empty()); + m_imp->m_affine = m_imp->m_matrixStack.back(); + m_imp->m_matrixStack.pop_back(); +} + +//------------------------------------------------------------------- + +void TFlash::multMatrix(const TAffine &aff) +{ + m_imp->m_affine *= aff; +} + +//------------------------------------------------------------------- + +void TFlash::putSound(TSoundTrackP st, int offset) +{ + m_imp->m_soundSize = 0; + m_imp->m_sound.clear(); + + TSoundTrackP st1 = st; + + if (st1->getBitPerSample() != c_soundBps || + st1->isSampleSigned() != c_soundIsSigned || + st1->getChannelCount() != c_soundChannelNum) + st1 = TSop::convert(st, TSoundTrackFormat(m_imp->m_soundRate, c_soundBps, c_soundChannelNum, c_soundIsSigned)); + else if (st1->getSampleRate() != (UINT)m_imp->m_soundRate) + st1 = TSop::resample(st1, m_imp->m_soundRate); + + int frameCount = st1->getSampleCount() / (st1->getSampleRate() / m_imp->m_frameRate); + if (!frameCount) + return; + int averagePerFrame = st1->getSampleCount() / frameCount; + int sampleCount = st1->getSampleCount(); + TINT32 firstSample = 0; + m_imp->m_soundOffset = offset; + + while (sampleCount > 0) { + sampleCount -= averagePerFrame; + if (sampleCount >= 0) { + TSoundTrackP snd = st1->extract(firstSample, firstSample + averagePerFrame - 1); + m_imp->m_sound.push_back(snd); + firstSample += averagePerFrame; + } else { + //deversamente dalla documentazione anche x il raw data deve essere + //sempre lo stesso il numero di campioni per ogni blocco aggiunto + //allo streaming audio + TSoundTrackP snd = st1->extract(firstSample, st1->getSampleCount() - 1); + snd = TSop::insertBlank(snd, snd->getSampleCount(), abs(sampleCount)); + m_imp->m_sound.push_back(snd); + } + } + m_imp->m_soundSize = m_imp->m_sound.size(); +} + +//------------------------------------------------------------------- + +void TFlash::writeMovie(FILE *fp) +{ + //m_imp->writeFrame(this, true); + + //if (m_imp->m_putCameraClip) + // m_imp->addCameraClip(); + + if (m_imp->m_properties.m_isCompressed.getValue()) + m_imp->m_tags.CreateCompressedMovie(fp, + FRect(0, 0, m_imp->m_lx * m_imp->m_tw, + m_imp->m_ly * m_imp->m_tw), + m_imp->m_frameRate); + else + m_imp->m_tags.CreateMovie(fp, + FRect(0, 0, m_imp->m_lx * m_imp->m_tw, + m_imp->m_ly * m_imp->m_tw), + m_imp->m_frameRate, + m_imp->m_version); +} + +//------------------------------------------------------------------- +void TFlash::drawRegion(const TRegion &r, int clippedShapes) +{ + int i; + vector polylines; + TPointD firstPoint, lastPoint; + + if (clippedShapes > 0 || m_imp->m_isMask) { + if (clippedShapes > 0) + m_imp->drawHangedObjects(); + + //setFillColor(TPixel::Black); + } + + TPointD pBegin, pEnd, pBeginRegion; + + pEnd = r.getEdge(r.getEdgeCount() - 1)->m_s->getPoint(r.getEdge(r.getEdgeCount() - 1)->m_w1); + for (i = 0; i < (int)r.getEdgeCount(); i++) + m_imp->addEdge(*r.getEdge(i), pBegin, pEnd); + + //m_imp->closeRegion(r.getEdgeCount()); + + m_imp->m_regionDepth++; + + for (i = 0; i < (int)r.getSubregionCount(); i++) { + TRegion &rr = *r.getSubregion(i); + pEnd = rr.getEdge(0)->m_s->getPoint(rr.getEdge(0)->m_w0); + for (int j = rr.getEdgeCount() - 1; j >= 0; j--) { + TEdge *e = rr.getEdge(j); + tswap(e->m_w0, e->m_w1); + m_imp->addEdge(*e, pBegin, pEnd); + tswap(e->m_w0, e->m_w1); + } + } + + m_imp->m_regionDepth--; + + if (clippedShapes > 0) { + m_imp->doDrawPolygon(m_imp->m_polylinesArray, clippedShapes); + //clearPointerContainer(m_imp->m_polylinesArray); + m_imp->m_polylinesArray.clear(); + m_imp->m_edgeMap.clear(); + m_imp->m_strokeMap.clear(); + m_imp->m_autocloseMap.clear(); + } +} + +//------------------------------------------------------------------- + +USHORT TFlash::buildImage(const TImageP img, bool isMask) +{ + double scalefactor; + return m_imp->buildImage(img, this, scalefactor, isMask); + assert(scalefactor == 1.0); +} + +void TFlash::beginFrame(int frameIndex) +{ + m_imp->m_frameData = new TFlash::Imp::FrameData; + m_imp->m_currFrameIndex = frameIndex; + assert(m_imp->m_matrixStack.empty()); + m_imp->m_affine = TAffine(); +} + +//------------------------------------------------------------------- +int TFlash::endFrame(bool isLast, int frameCountLoader, bool lastScene) +{ + m_imp->writeFrame(this, isLast, frameCountLoader, lastScene); + if (m_imp->m_oldFrameData) + delete m_imp->m_oldFrameData; + m_imp->m_oldFrameData = m_imp->m_frameData; + m_imp->m_frameData = 0; + + //QString msg = "mem: " + QString::number(m_imp->m_totMem/1024.0)+"\n"; + //TSystem::outputDebug(msg.toStdString()); + return m_imp->m_currFrameIndex; +} + +//------------------------------------------------------------------- + +//void TFlash::addPauseAtStart() +//{ +// m_imp->addPauseAtStart(); +//} + +//------------------------------------------------------------------- + +void TFlash::enableMask() +{ + m_imp->m_maskEnabled = true; +} + +//------------------------------------------------------------------- + +void TFlash::disableMask() +{ + m_imp->m_maskEnabled = false; +} + +//------------------------------------------------------------------- + +void TFlash::beginMask() +{ + m_imp->beginMask(); +} + +//------------------------------------------------------------------- + +void TFlash::endMask() +{ + m_imp->endMask(); +} + +//------------------------------------------------------------------- +void TFlash::draw(const TImageP img, const TColorFunction *cf) +{ + //std::map::iterator it; + + if (m_imp->m_currMask) { + if (img->getType() == TImage::VECTOR) + m_imp->m_currMask->mergeImage((TVectorImageP)img, m_imp->m_affine); + } else if (img) + m_imp->m_frameData->push_back(FlashImageData(m_imp->m_affine, img, cf ? cf->clone() : 0, false, m_imp->m_maskEnabled)); +} + +//------------------------------------------------------------------- + +wstring TFlash::getLineQuality() +{ + return m_imp->m_properties.m_lineQuality.getValue(); +} + +//------------------------------------------------------------------- + +bool TFlash::drawOutline(TStroke *s) +{ + assert(m_imp->m_polyData.m_type == Solid); + + m_imp->drawHangedObjects(); + + TThickPoint p0 = s->getThickPoint(0.0), p1 = s->getThickPoint(1.0); + + if (tdistance(p0, p1) < (p0.thick + p1.thick)) //pezza infame!nelle curve che si autochiudono, lo sweepboundary fa casini. + //le splitto in due pezzi, e per poter metterli nello steso poligono + //senza vedere le sovrapposizioni cambio leggermente il colore al secondo....eh eh eh + { + TStroke *s0 = new TStroke(), *s1 = new TStroke(); + + s->split(0.5, *s0, *s1); + m_imp->drawOutline(s0, true); + int aux = m_imp->m_polyData.m_color1.b; + m_imp->m_polyData.m_color1.b = (m_imp->m_polyData.m_color1.b == 255) ? 254 : m_imp->m_polyData.m_color1.b + 1; + + //m_imp->drawHangedObjects(); + m_imp->drawOutline(s1, false); + m_imp->m_polyData.m_color1.b = aux; + //m_imp->drawHangedObjects(); + m_imp->m_strokesToBeDeleted.push_back(s0); + m_imp->m_strokesToBeDeleted.push_back(s1); + return true; + } + + return m_imp->drawOutline(s, true); +} + +//------------------------------------------------------------- diff --git a/toonz/sources/common/tvrender/tfont_mac.cpp b/toonz/sources/common/tvrender/tfont_mac.cpp new file mode 100644 index 0000000..13fda58 --- /dev/null +++ b/toonz/sources/common/tvrender/tfont_mac.cpp @@ -0,0 +1,852 @@ + + +#ifndef __LP64__ + +#include +#include +#include +#include "tmathutil.h" +#include "tdebugmessage.h" +#include "tfont.h" +#include "tstroke.h" +#include "tcurves.h" +#include "tconvert.h" +#include "tvectorimage.h" + +using namespace std; + +//----------------------------------------- structures ------------------------------------------------------------------- + +typedef struct { + Float32Point origin; // The origin of the current glyph + Boolean first; // Keeps track of which segment is first in a glyph + Float32Point current; // The current pen position (used to filter degenerate cases) + + float adv; + TVectorImageP m_image; + std::vector m_points; + +} MyCurveCallbackData; + +typedef struct { + ATSGlyphRef glyphID; // The glyphID. This is simply an index into a table in the font. + Float32Point relativeOrigin; // The origin of this glyph -- relative to the origin of the line. +} MyGlyphRecord; + +//----------------------------------------- callback--------------------------------------------------------------------- + +OSStatus MyQuadraticLineProc(const Float32Point *pt1, const Float32Point *pt2, void *callBackDataPtr) +{ + /* + // Adjust the points according to the glyph origin + float x1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt1->x; + float y1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt1->y; + float x2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt2->x; + float y2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt2->y; + */ + + MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr; + + if (data->m_points.empty()) + data->m_points.push_back(TThickPoint(pt1->x, pt1->y, 0)); + //else + //assert(isAlmostEqual(pt1 e back) + + TThickPoint lastPoint = TThickPoint(pt2->x, pt2->y, 0); + data->m_points.push_back((data->m_points.back() + lastPoint) * 0.5); + data->m_points.push_back(lastPoint); + + return noErr; +} + +OSStatus MyQuadraticCurveProc(const Float32Point *pt1, const Float32Point *controlPt, const Float32Point *pt2, void *callBackDataPtr) +{ + /* + // Adjust the points according to the glyph origin + float x1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt1->x; + float y1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt1->y; + float x2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt2->x; + float y2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt2->y; + float cpx = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + controlPt->x; + float cpy = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + controlPt->y; + */ + MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr; + + if (data->m_points.empty()) + data->m_points.push_back(TThickPoint(pt1->x, pt1->y, 0)); + //else + //assert(isAlmostEqual(pt1 e back) + + data->m_points.push_back(TThickPoint(controlPt->x, controlPt->y, 0)); + data->m_points.push_back(TThickPoint(pt2->x, pt2->y, 0)); + + return noErr; +} + +OSStatus MyQuadraticNewPathProc(void *callBackDataPtr) +{ + assert(((MyCurveCallbackData *)callBackDataPtr)->m_points.empty()); + return noErr; +} + +OSStatus MyQuadraticClosePathProc(void *callBackDataPtr) +{ + MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr; + + assert(data->m_points.size() >= 3 && data->m_points.size() & 1); //il numero di punti di controllo devono essere dispari e >= 3 + + TStroke *stroke = new TStroke(data->m_points); + stroke->setSelfLoop(true); + + data->m_points.clear(); + data->m_image->addStroke(stroke); + + return noErr; +} + +//------------------------------------------------------------------------------------------------------------------ + +void GetGlyphIDsAndPositions(ATSUTextLayout iLayout, UniCharArrayOffset iStart, UniCharCount iLength, MyGlyphRecord **oGlyphRecordArray, ItemCount *oNumGlyphs) +{ + // This block of code uses the new Direct Access APIs, which are only available on Mac OS X 10.2 and later systems + // + + ATSLayoutRecord *layoutRecords; + ItemCount numRecords; + Fixed *deltaYs; + ItemCount numDeltaYs; + unsigned int i; + OSStatus status; + + // Get the arrays of glyph information + status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( + iLayout, iStart, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void **)&layoutRecords, &numRecords); + assert(status == noErr); + + status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( + iLayout, iStart, + kATSUDirectDataBaselineDeltaFixedArray, + (void **)&deltaYs, &numDeltaYs); + assert(status == noErr); + + // Build the array of MyGlyphRecords + *oGlyphRecordArray = (MyGlyphRecord *)malloc(numRecords * sizeof(MyGlyphRecord)); + *oNumGlyphs = numRecords; + + for (i = 0; i < *oNumGlyphs; i++) { + // Fill in the glyphID + (*oGlyphRecordArray)[i].glyphID = layoutRecords[i].glyphID; + + // Set up the relative origin of the glyph + // + // The real position is the x coordinate of the glyph, relative to the beginning of the line + // The baseline delta (deltaY), if any, is the y coordinate of the glyph, relative to the baseline + // + (*oGlyphRecordArray)[i].relativeOrigin.x = Fix2X(layoutRecords[i].realPos); + + if (deltaYs == NULL) { + (*oGlyphRecordArray)[i].relativeOrigin.y = 0.0; + } else { + (*oGlyphRecordArray)[i].relativeOrigin.y = 0.0 - Fix2X(deltaYs[i]); + } + } + + // Free the arrays of glyph information + if (deltaYs != NULL) { + status = ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataBaselineDeltaFixedArray, (void **)&deltaYs); + assert(status == noErr); + } + status = ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords); + assert(status == noErr); +} + +void drawQuadratics(ATSUTextLayout iLayout, ATSUStyle iStyle, UniCharArrayOffset start, UniCharCount length, MyCurveCallbackData &data) +{ + //boh ---------------- + Fixed penX = 0; + Fixed penY = 0; + // ------------------- + + MyGlyphRecord *glyphRecordArray; + ItemCount numGlyphs; + ATSQuadraticNewPathUPP newPathProc; + ATSQuadraticLineUPP lineProc; + ATSQuadraticCurveUPP curveProc; + ATSQuadraticClosePathUPP closePathProc; + + OSStatus status; + unsigned int i; + + // Create the Quadratic callbacks + newPathProc = NewATSQuadraticNewPathUPP(MyQuadraticNewPathProc); + lineProc = NewATSQuadraticLineUPP(MyQuadraticLineProc); + curveProc = NewATSQuadraticCurveUPP(MyQuadraticCurveProc); + closePathProc = NewATSQuadraticClosePathUPP(MyQuadraticClosePathProc); + + // Get the array of glyph information + GetGlyphIDsAndPositions(iLayout, start, length, &glyphRecordArray, &numGlyphs); + + // Loop over all the glyphs + for (i = 0; i < numGlyphs; i++) { + + // Set up the absolute origin of the glyph + data.origin.x = Fix2X(penX) + glyphRecordArray[i].relativeOrigin.x; + data.origin.y = Fix2X(penY) + glyphRecordArray[i].relativeOrigin.y; + + // Reset state for quadratic drawing (the callbacks only do a MoveTo on the very first segment) + data.first = true; + + // If this is a deleted glyph (-1), don't draw it. Otherwise, go ahead. + if (glyphRecordArray[i].glyphID != kATSDeletedGlyphcode) { + status = ATSUGlyphGetQuadraticPaths(iStyle, glyphRecordArray[i].glyphID, newPathProc, lineProc, curveProc, closePathProc, &data, &status); + assert(status == noErr); + } + } + // Free the array of glyph information + free(glyphRecordArray); + + // Dispose of the Quadratic callbacks + + DisposeATSQuadraticNewPathUPP(newPathProc); + DisposeATSQuadraticLineUPP(lineProc); + DisposeATSQuadraticCurveUPP(curveProc); + DisposeATSQuadraticClosePathUPP(closePathProc); +} + +//============================================================================= + +struct TFont::Impl { + bool m_hasKerning; + int m_hasVertical; + + // KerningPairs m_kerningPairs; + + ATSUStyle m_style; + ATSUFontID m_fontId; + ATSUTextLayout m_layout; + Fixed m_size; + int m_ascender; + int m_descender; + + Impl(ATSUFontID fontId, int size); + ~Impl(); + + //void getChar(); +}; + +//----------------------------------------------------------------------------- + +TFont::TFont(ATSUFontID fontId, int size) +{ + m_pimpl = new Impl(fontId, size); +} + +//----------------------------------------------------------------------------- + +TFont::~TFont() +{ + delete m_pimpl; +} + +//----------------------------------------------------------------------------- + +TFont::Impl::Impl(ATSUFontID fontId, int size) + : m_fontId(fontId), m_size(Long2Fix(size)) +{ + OSStatus status; + + long response; + status = Gestalt(gestaltATSUFeatures, &response); + assert(response & gestaltATSUDirectAccess); + + status = ATSUCreateStyle(&m_style); + assert(status == noErr); + + ATSUAttributeTag tags[2]; + ByteCount sizes[2]; + ATSUAttributeValuePtr values[2]; + + tags[0] = kATSUFontTag; + sizes[0] = sizeof(ATSUFontID); + values[0] = &fontId; + + tags[1] = kATSUSizeTag; + sizes[1] = sizeof(Fixed); + values[1] = &m_size; + + status = ATSUSetAttributes(m_style, 2, tags, sizes, values); + //assert(status==noErr); + + UniChar dummyStr[] = {'H', 'e', 'l', 'l', 'o'}; + UniCharCount length = sizeof(dummyStr) / sizeof(UniChar); + + status = ATSUCreateTextLayoutWithTextPtr( + dummyStr, kATSUFromTextBeginning, kATSUToTextEnd, length, 1, + &length, &m_style, &m_layout); + //assert(status==noErr); + + ATSTrapezoid glyphBounds; + status = ATSUGetGlyphBounds(m_layout, 0, 0, + kATSUFromTextBeginning, kATSUToTextEnd, kATSUseFractionalOrigins, + 1, &glyphBounds, NULL); + + m_ascender = -FixedToInt(glyphBounds.upperLeft.y); + assert(m_ascender > 0); + m_descender = -FixedToInt(glyphBounds.lowerLeft.y); + assert(m_descender < 0); +} + +//----------------------------------------------------------------------------- + +TFont::Impl::~Impl() +{ +} + +//----------------------------------------------------------------------------- + +TPoint TFont::drawChar(TVectorImageP &image, wchar_t charcode, wchar_t nextCharCode) const +{ + OSStatus status; + + UniChar subString[2]; + subString[0] = charcode; + subString[1] = 0 /*nextCharCode*/; + UniCharCount length = sizeof(subString) / sizeof(UniChar); + + status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd, + length, 1, + &length, &(m_pimpl->m_style), &(m_pimpl->m_layout)); + assert(status == noErr); + + MyCurveCallbackData data; + data.m_image = image; + + drawQuadratics(m_pimpl->m_layout, m_pimpl->m_style, kATSUFromTextBeginning, kATSUToTextEnd, data); + image->transform(TScale(1, -1)); + + image->group(0, image->getStrokeCount()); + + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- +namespace +{ + +void appDrawChar(TRasterGR8P &outImage, TFont::Impl *pimpl, wchar_t charcode) +{ + OSStatus status; + UniChar subString[2]; + subString[0] = charcode; + subString[1] = 0; + UniCharCount length = sizeof(subString) / sizeof(UniChar); + + status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd, + length, 1, + &length, &(pimpl->m_style), &(pimpl->m_layout)); + assert(status == noErr); + + ATSTrapezoid glyphBounds; + status = ATSUGetGlyphBounds(pimpl->m_layout, 0, 0, + kATSUFromTextBeginning, kATSUToTextEnd, kATSUseFractionalOrigins, + 1, &glyphBounds, NULL); + + int height = FixedToInt(glyphBounds.lowerLeft.y) - FixedToInt(glyphBounds.upperLeft.y); + int width = tmax(FixedToInt(glyphBounds.lowerRight.x), FixedToInt(glyphBounds.upperRight.x)) - + tmin(FixedToInt(glyphBounds.lowerLeft.x), FixedToInt(glyphBounds.upperLeft.x)); + + outImage = TRasterGR8P(width, height); + TPixelGR8 bgp; + bgp.value = 255; + outImage->fill(bgp); + void *data = outImage->getRawData(); + + CGColorSpaceRef grayColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); + CGContextRef gContext = CGBitmapContextCreate(data, width, height, 8, width, grayColorSpace, kCGImageAlphaNone); + +#if defined(DEBUG) || defined(_DEBUG) + + int bpc = CGBitmapContextGetBitsPerComponent(gContext); + if (bpc != 8) + std::cout << "BitsPerComponent: " << bpc << std::endl; + + int bpp = CGBitmapContextGetBitsPerPixel(gContext); + if (bpp != 8) + std::cout << "BitsPerPixel: " << bpp << std::endl; + + int bytesPerRow = CGBitmapContextGetBytesPerRow(gContext); + int newWidth = CGBitmapContextGetWidth(gContext); + if (bytesPerRow != width || newWidth != width) + std::cout << "BytesPerRow: " << bytesPerRow + << " Old width= " << width + << " New width= " << newWidth << std::endl; + + int newHeight = CGBitmapContextGetHeight(gContext); + if (newHeight != height) + std::cout << " Old height= " << height << " New height= " << newHeight << std::endl; + + assert(CGBitmapContextGetColorSpace(gContext) == grayColorSpace); + +#endif + + ATSUAttributeTag tags[1]; + ByteCount sizes[1]; + ATSUAttributeValuePtr values[1]; + + tags[0] = kATSUCGContextTag; + sizes[0] = sizeof(CGContextRef); + values[0] = &gContext; + status = ATSUSetLayoutControls(pimpl->m_layout, 1, tags, sizes, values); + assert(status == noErr); + + ATSUDrawText(pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, 0, glyphBounds.lowerLeft.y); +} +} +//----------------------------------------------------------------------------- + +TPoint TFont::drawChar(TRasterGR8P &outImage, TPoint &unused, wchar_t charcode, wchar_t nextCharCode) const +{ + appDrawChar(outImage, m_pimpl, charcode); + outImage->yMirror(); + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- + +TPoint TFont::drawChar(TRasterCM32P &outImage, TPoint &unused, int inkId, wchar_t charcode, wchar_t nextCharCode) const +{ + TRasterGR8P grayAppImage; + appDrawChar(grayAppImage, m_pimpl, charcode); + + int lx = grayAppImage->getLx(); + int ly = grayAppImage->getLy(); + + outImage = TRasterCM32P(lx, ly); + + assert(TPixelCM32::getMaxTone() == 255); + TPixelCM32 bgColor(0, 0, TPixelCM32::getMaxTone()); + grayAppImage->lock(); + outImage->lock(); + int ty = 0; + for (int gy = ly - 1; gy >= 0; --gy, ++ty) { + TPixelGR8 *srcPix = grayAppImage->pixels(gy); + TPixelCM32 *tarPix = outImage->pixels(ty); + for (int x = 0; x < lx; ++x) { + int tone = srcPix->value; + + if (tone == 255) + *tarPix = bgColor; + else + *tarPix = TPixelCM32(inkId, 0, tone); + + ++srcPix; + ++tarPix; + } + } + grayAppImage->unlock(); + outImage->unlock(); + + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- + +TPoint TFont::getDistance(wchar_t firstChar, wchar_t secondChar) const +{ + OSStatus status; + UniChar subString[2]; + subString[0] = firstChar; + subString[1] = secondChar; + UniCharCount length = sizeof(subString) / sizeof(UniChar); + + status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd, + length, 1, + &length, &(m_pimpl->m_style), &(m_pimpl->m_layout)); + assert(status == noErr); + + MyGlyphRecord *glyphRecordArray; + ItemCount numGlyphs; + + // Get the array of glyph information + GetGlyphIDsAndPositions(m_pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, &glyphRecordArray, &numGlyphs); + + assert(numGlyphs >= 2); + + assert(glyphRecordArray[0].relativeOrigin.x == 0); + int advance = (int)(glyphRecordArray[1].relativeOrigin.x - glyphRecordArray[0].relativeOrigin.x); + if (advance == 0) { + subString[1] = 0; + status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd, + length, 1, + &length, &(m_pimpl->m_style), &(m_pimpl->m_layout)); + + GetGlyphIDsAndPositions(m_pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, &glyphRecordArray, &numGlyphs); + advance = (int)(glyphRecordArray[1].relativeOrigin.x - glyphRecordArray[0].relativeOrigin.x); + } + return TPoint(advance, 0); +} + +//----------------------------------------------------------------------------- + +int TFont::getMaxHeight() const +{ + return m_pimpl->m_ascender - m_pimpl->m_descender; +} + +//----------------------------------------------------------------------------- + +int TFont::getMaxWidth() const +{ + assert(!"not implemented yet"); + return 100; +} +//----------------------------------------------------------------------------- + +int TFont::getLineAscender() const +{ + return m_pimpl->m_ascender; +} + +//----------------------------------------------------------------------------- + +int TFont::getLineDescender() const +{ + return m_pimpl->m_descender; +} + +//----------------------------------------------------------------------------- + +bool TFont::hasKerning() const +{ + return true; +} + +//----------------------------------------------------------------------------- + +bool TFont::hasVertical() const +{ + return false; +} + +//----------------------------------------------------------------------------- + +#include +#include + +typedef std::map FontFamily; + +typedef std::map FamilyMap; + +struct TFontManager::Impl { + FamilyMap m_families; + bool m_loaded; + ATSUFontID m_currentAtsuFontId; + TFont *m_currentFont; + wstring m_currentFamily; + wstring m_currentTypeface; + int m_size; + + Impl() + : m_currentAtsuFontId(0), m_currentFont(0), m_loaded(false), m_size(70) + { + } + + bool setFontName(ATSUFontID fontId, int platform, int script, int lang); + bool addFont(ATSUFontID); + void loadFontNames(); + bool setFont(std::wstring family, std::wstring style); +}; + +using namespace std; + +bool TFontManager::Impl::setFontName(ATSUFontID fontId, int platform, int script, int lang) +{ + ByteCount oActualNameLength; + ItemCount oFontCount; + OSStatus status; + + char *buffer = 0; + char *buffer2 = 0; + + // chiedo la lunhezza del Full Family Name per allocare il buffer + status = ATSUFindFontName(fontId, + kFontFullName, + platform, + script, + lang, + 0, 0, + &oActualNameLength, + 0); + + if (status != noErr || oActualNameLength <= 1) + return false; + + buffer = new char[oActualNameLength + 1]; + // chiedo il Full Family Name + status = ATSUFindFontName(fontId, + kFontFullName, + platform, + script, + lang, + oActualNameLength, buffer, + &oActualNameLength, + &oFontCount); + + if (status != noErr || oActualNameLength <= 1 || buffer[0] == '\0') { + delete[] buffer; + return false; + } + buffer[oActualNameLength] = '\0'; + + //------------------- + + // chiedo la lunhezza del Typeface Name per allocare il buffer + status = ATSUFindFontName(fontId, + kFontStyleName, + platform, + script, + lang, + 0, 0, + &oActualNameLength, + 0); + + if (status != noErr || oActualNameLength <= 1) { + delete[] buffer; + return false; + } + buffer2 = new char[oActualNameLength + 1]; + + // chiedo il Typeface Name + status = ATSUFindFontName(fontId, + kFontStyleName, + platform, + script, + lang, + oActualNameLength, buffer2, + &oActualNameLength, + &oFontCount); + + if (status != noErr || oActualNameLength <= 1 || buffer2[0] == '\0') { + delete[] buffer; + delete[] buffer2; + return false; + } else + buffer2[oActualNameLength] = '\0'; + + string s_family(buffer); + FontFamily &family = m_families[s_family]; + family[string(buffer2)] = fontId; + delete[] buffer; + delete[] buffer2; + return true; +} + +bool TFontManager::Impl::addFont(ATSUFontID fontId) +{ + int platform, script, lang; + + // per ottimizzare, ciclo solo sui valori + // piu' comuni + for (lang = -1; lang <= 0; lang++) + for (platform = -1; platform <= 1; platform++) + for (script = -1; script <= 0; script++) + if (setFontName(fontId, platform, script, lang)) + return true; + + //poi li provo tutti + for (lang = -1; lang <= 139; lang++) + for (script = -1; script <= 32; script++) + for (platform = -1; platform <= 4; platform++) { + // escludo quelli nel tri-ciclo for precedente. + // Purtoppo si deve fare cosi: + // non si puo' fare partendo con indici piu' alti nei cicli for! + if (-1 <= lang && lang <= 0 && + -1 <= script && script <= 0 && + -1 <= platform && platform <= 1) + continue; + + if (setFontName(fontId, platform, script, lang)) + return true; + } + + return false; +} + +void TFontManager::Impl::loadFontNames() +{ + if (m_loaded) + return; + + ItemCount oFontCount, fontCount; + ATSUFontCount(&oFontCount); + fontCount = oFontCount; + ATSUFontID *oFontIDs = new ATSUFontID[fontCount]; + ATSUGetFontIDs(oFontIDs, fontCount, &oFontCount); + assert(fontCount == oFontCount); + + for (unsigned int i = 0; i < fontCount; i++) + addFont(oFontIDs[i]); + + delete[] oFontIDs; + m_loaded = true; +} + +bool TFontManager::Impl::setFont(std::wstring family, std::wstring typeface) +{ + if (family == m_currentFamily && (typeface == m_currentTypeface || typeface == L"")) + return false; + + FamilyMap::iterator family_it = m_families.find(toString(family)); + if (family_it == m_families.end()) + throw TFontCreationError(); + + m_currentFamily = family; + FontFamily::iterator style_it; + if (typeface == L"") { + style_it = ((*family_it).second).find(toString(m_currentTypeface)); + if (style_it == (*family_it).second.end()) + style_it = ((*family_it).second).begin(); + + typeface = toWideString(style_it->first); + } else + style_it = ((*family_it).second).find(toString(typeface)); + + if (style_it == (*family_it).second.end()) + throw TFontCreationError(); + + m_currentTypeface = typeface; + m_currentAtsuFontId = (*style_it).second; + return true; +} + +//--------------------------------------------------------- + +TFontManager::TFontManager() +{ + m_pimpl = new TFontManager::Impl(); +} + +//--------------------------------------------------------- + +TFontManager::~TFontManager() +{ + delete m_pimpl; +} + +//--------------------------------------------------------- + +TFontManager *TFontManager::instance() +{ + static TFontManager theManager; + return &theManager; +} + +//--------------------------------------------------------- + +void TFontManager::loadFontNames() +{ + m_pimpl->loadFontNames(); +} + +//--------------------------------------------------------- + +void TFontManager::setFamily(const wstring family) +{ + bool changed = m_pimpl->setFont(family, L""); + if (changed) { + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size); + } +} + +//--------------------------------------------------------- + +void TFontManager::setTypeface(const wstring typeface) +{ + bool changed = m_pimpl->setFont(m_pimpl->m_currentFamily, typeface); + if (changed) { + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size); + } +} + +//--------------------------------------------------------- + +void TFontManager::setSize(int size) +{ + if (m_pimpl->m_size != size) { + m_pimpl->m_size = size; + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size); + } +} + +//--------------------------------------------------------- + +wstring TFontManager::getCurrentFamily() const +{ + return m_pimpl->m_currentFamily; +} + +//--------------------------------------------------------- + +wstring TFontManager::getCurrentTypeface() const +{ + return m_pimpl->m_currentTypeface; +} + +//--------------------------------------------------------- + +TFont *TFontManager::getCurrentFont() +{ + if (m_pimpl->m_currentFont) + return m_pimpl->m_currentFont; + + if (!m_pimpl->m_currentFont) + loadFontNames(); + + assert(!m_pimpl->m_families.empty()); + setFamily(toWideString(m_pimpl->m_families.begin()->first)); + + return m_pimpl->m_currentFont; +} + +//--------------------------------------------------------- + +void TFontManager::getAllFamilies(vector &families) const +{ + families.clear(); + families.reserve(m_pimpl->m_families.size()); + + FamilyMap::iterator it = m_pimpl->m_families.begin(); + for (; it != m_pimpl->m_families.end(); ++it) { + families.push_back(toWideString(it->first)); + } +} + +//--------------------------------------------------------- + +void TFontManager::getAllTypefaces(vector &typefaces) const +{ + typefaces.clear(); + FamilyMap::iterator it_family = m_pimpl->m_families.find(toString(m_pimpl->m_currentFamily)); + if (it_family == m_pimpl->m_families.end()) + return; + FontFamily &typefaceSet = it_family->second; + + typefaces.reserve(typefaceSet.size()); + FontFamily::iterator it_typeface = typefaceSet.begin(); + for (; it_typeface != typefaceSet.end(); ++it_typeface) { + typefaces.push_back(toWideString(it_typeface->first)); + } +} + +//--------------------------------------------------------- + +void TFontManager::setVertical(bool vertical) {} + +//--------------------------------------------------------- + +#endif diff --git a/toonz/sources/common/tvrender/tfont_nt.cpp b/toonz/sources/common/tvrender/tfont_nt.cpp new file mode 100644 index 0000000..44af3e1 --- /dev/null +++ b/toonz/sources/common/tvrender/tfont_nt.cpp @@ -0,0 +1,702 @@ + + +// character_manager.cpp: implementation of the TFont class. +// +////////////////////////////////////////////////////////////////////// + +#include "tpixelgr.h" +#include "tfont.h" +#include "tstroke.h" +//#include "tcurves.h" +#include "traster.h" +#include +#include +#include +//#include +#include +//#include +#include "tvectorimage.h" +using namespace std; + +//============================================================================= + +typedef map WindowsFontTable; +typedef map, int> KerningPairs; + +//============================================================================= + +struct TFont::Impl { + + bool m_hasKerning; + bool m_hasVertical; + HFONT m_font; + HDC m_hdc; + TEXTMETRICW m_metrics; + KerningPairs m_kerningPairs; + + Impl(const LOGFONTW &font, HDC hdc); + ~Impl(); +}; + +//----------------------------------------------------------------------------- + +TFont::TFont(const LOGFONTW &font, HDC hdc) +{ + m_pimpl = new Impl(font, hdc); +} + +//----------------------------------------------------------------------------- + +TFont::~TFont() +{ + delete m_pimpl; +} + +//----------------------------------------------------------------------------- + +TFont::Impl::Impl(const LOGFONTW &logfont, HDC hdc) + : m_hdc(hdc) +{ + m_font = CreateFontIndirectW(&logfont); + + if (!m_font) + throw TFontCreationError(); + + HGDIOBJ hObj = SelectObject(hdc, m_font); + if (!hObj || hObj == HGDI_ERROR) + throw TFontCreationError(); + + if (!GetTextMetricsW(hdc, &m_metrics)) + throw TFontCreationError(); + + DWORD pairsCount = GetKerningPairsW(hdc, 0, 0); + if (pairsCount) { + m_hasKerning = true; + KERNINGPAIR *tempKernPairs = new KERNINGPAIR[pairsCount]; + GetKerningPairsW(hdc, pairsCount, tempKernPairs); + for (UINT i = 0; i < pairsCount; i++) { + pair key = make_pair(tempKernPairs[i].wFirst, tempKernPairs[i].wSecond); + m_kerningPairs[key] = tempKernPairs[i].iKernAmount; + } + delete[] tempKernPairs; + } else + m_hasKerning = false; + + m_hasVertical = (logfont.lfFaceName)[0] == '@'; +} + +//----------------------------------------------------------------------------- + +TFont::Impl::~Impl() +{ + //delete m_advances; + DeleteObject(m_font); +} + +//----------------------------------------------------------------------------- + +namespace +{ +inline TThickPoint toThickPoint(POINTFX point) +{ + double app1 = point.x.value + ((double)point.x.fract) / (std::numeric_limits::max)(); + double app2 = point.y.value + ((double)point.y.fract) / (std::numeric_limits::max)(); + return TThickPoint(app1, app2, 0); +} +} + +//----------------------------------------------------------------------------- + +TPoint TFont::drawChar(TVectorImageP &image, wchar_t charcode, wchar_t nextCharCode) const + +{ + + GLYPHMETRICS gm; + MAT2 mat2; + mat2.eM11.fract = 0; + mat2.eM12.fract = 0; + mat2.eM21.fract = 0; + mat2.eM22.fract = 0; + mat2.eM11.value = 1; + mat2.eM12.value = 0; + mat2.eM21.value = 0; + mat2.eM22.value = 1; + + vector points; + + UINT j = 0; + + DWORD charMemorySize = GetGlyphOutlineW(m_pimpl->m_hdc, charcode, GGO_NATIVE, &gm, 0, 0, &mat2); + if (charMemorySize == GDI_ERROR) { + assert(0); + return TPoint(); + } + + LPVOID lpvBuffer = new char[charMemorySize]; + + charMemorySize = GetGlyphOutlineW(m_pimpl->m_hdc, charcode, GGO_NATIVE, &gm, charMemorySize, lpvBuffer, &mat2); + if (charMemorySize == GDI_ERROR) { + assert(0); + return TPoint(); + } + + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)lpvBuffer; + + while ((char *)header < (char *)lpvBuffer + charMemorySize) { + points.clear(); + TThickPoint startPoint = toThickPoint(header->pfxStart); + points.push_back(startPoint); + + if (header->dwType != TT_POLYGON_TYPE) { + assert(0); + } + int memorySize = header->cb; + + TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1); + + while ((char *)curve < (char *)header + memorySize) { + switch (curve->wType) { + case TT_PRIM_LINE: + + for (j = 0; j < curve->cpfx; j++) { + TThickPoint p0 = points.back(); + TThickPoint p1 = toThickPoint(((*curve).apfx[j])); + points.push_back((p0 + p1) * 0.5); + points.push_back(p1); + } + + break; + + case TT_PRIM_QSPLINE: + + for (j = 0; (int)j + 2 < curve->cpfx; j++) { + TThickPoint p1 = toThickPoint(((*curve).apfx[j])); + TThickPoint p2 = toThickPoint(((*curve).apfx[j + 1])); + + points.push_back(p1); + points.push_back((p1 + p2) * 0.5); + } + points.push_back(toThickPoint(((*curve).apfx[j++]))); + points.push_back(toThickPoint(((*curve).apfx[j++]))); + + break; + case TT_PRIM_CSPLINE: + assert(0); + break; + default: + assert(0); + } + + curve = (TTPOLYCURVE *)(&(curve->apfx)[j]); + } + + TThickPoint p0 = points.back(); + if (!isAlmostZero(p0.x - startPoint.x) || !isAlmostZero(p0.y - startPoint.y)) { + points.push_back((p0 + startPoint) * 0.5); + points.push_back(startPoint); + } + + TStroke *stroke = new TStroke(); + stroke->reshape(&(points[0]), points.size()); + stroke->setSelfLoop(true); + image->addStroke(stroke); + + header = (TTPOLYGONHEADER *)curve; + } + + delete[] lpvBuffer; + + image->group(0, image->getStrokeCount()); + + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- + +namespace +{ + +TPoint appDrawChar(TRasterGR8P &outImage, HDC hdc, wchar_t charcode) +{ + GLYPHMETRICS gm; + MAT2 mat2; + mat2.eM11.fract = 0; + mat2.eM12.fract = 0; + mat2.eM21.fract = 0; + mat2.eM22.fract = 0; + mat2.eM11.value = 1; + mat2.eM12.value = 0; + mat2.eM21.value = 0; + mat2.eM22.value = 1; + + DWORD charMemorySize = GetGlyphOutlineW(hdc, charcode, GGO_GRAY8_BITMAP, &gm, 0, 0, &mat2); + + if (charMemorySize == GDI_ERROR) { + assert(0); + } + + int lx = gm.gmBlackBoxX; + int ly = gm.gmBlackBoxY; + + int wrap = ((lx + 3) >> 2) << 2; + + TRasterGR8P appImage = TRasterGR8P(wrap, ly); + appImage->clear(); + outImage = appImage->extract(0, 0, lx - 1, ly - 1); + outImage->lock(); + GetGlyphOutlineW(hdc, charcode, GGO_GRAY8_BITMAP, &gm, wrap * ly, outImage->getRawData(), &mat2); + outImage->unlock(); + TPoint glyphOrig; + glyphOrig.x = gm.gmptGlyphOrigin.x; + glyphOrig.y = gm.gmptGlyphOrigin.y; + return glyphOrig; +} +} + +//----------------------------------------------------------------------------- +//valori compresi tra 0 e 64 (si si, proprio 64 e non 63: sono 65 valori) + +TPoint TFont::drawChar(TRasterGR8P &outImage, TPoint &glyphOrigin, wchar_t charcode, wchar_t nextCharCode) const +{ + TRasterGR8P grayAppImage; + + TPoint glyphOrig = appDrawChar(grayAppImage, m_pimpl->m_hdc, charcode); + + if (glyphOrig.x < 0) { + glyphOrigin.x = glyphOrig.x; + glyphOrig.x = 0; + } else + glyphOrigin.x = 0; + + if (glyphOrig.y < 0) { + glyphOrigin.y = glyphOrig.y; + glyphOrig.y = 0; + } else + glyphOrigin.y = 0; + + int srcLx = grayAppImage->getLx(); + int srcLy = grayAppImage->getLy(); + + int dstLx = srcLx + glyphOrig.x; + int dstLy = getMaxHeight(); + + outImage = TRasterGR8P(dstLx, dstLy); + outImage->clear(); + + int ty = m_pimpl->m_metrics.tmDescent - 1 + glyphOrig.y; + assert(ty < dstLy); + assert(ty >= srcLy - 1); + grayAppImage->lock(); + outImage->lock(); + + for (int sy = 0; sy < srcLy; ++sy, --ty) { + TPixelGR8 *srcPix = grayAppImage->pixels(sy); + TPixelGR8 *tarPix = outImage->pixels(ty) + glyphOrig.x; + for (int x = 0; x < srcLx; ++x) { + assert(srcPix->value < 65); + + switch (srcPix->value) { + case 0: + tarPix->value = 0; + DEFAULT: + tarPix->value = (srcPix->value << 2) - 1; + } + ++srcPix; + ++tarPix; + } + } + grayAppImage->unlock(); + outImage->unlock(); + + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- + +TPoint TFont::drawChar(TRasterCM32P &outImage, TPoint &glyphOrigin, int inkId, wchar_t charcode, wchar_t nextCharCode) const +{ + TRasterGR8P grayAppImage; + TPoint glyphOrig = appDrawChar(grayAppImage, m_pimpl->m_hdc, charcode); + + if (glyphOrig.x < 0) { + glyphOrigin.x = glyphOrig.x; + glyphOrig.x = 0; + } else + glyphOrigin.x = 0; + + if (glyphOrig.y < 0) { + glyphOrigin.y = glyphOrig.y; + glyphOrig.y = 0; + } else + glyphOrigin.y = 0; + + int srcLx = grayAppImage->getLx(); + int srcLy = grayAppImage->getLy(); + + int dstLx = srcLx + glyphOrig.x; + int dstLy = getMaxHeight(); + + outImage = TRasterCM32P(dstLx, dstLy); + outImage->clear(); + + assert(TPixelCM32::getMaxTone() == 255); + // TPixelCM32 bgColor(BackgroundStyle,BackgroundStyle,TPixelCM32::getMaxTone()); + TPixelCM32 bgColor; + + int ty = m_pimpl->m_metrics.tmDescent - 1 + glyphOrig.y; + assert(ty < dstLy); + assert(ty >= srcLy - 1); + grayAppImage->lock(); + outImage->lock(); + + for (int sy = 0; sy < srcLy; ++sy, --ty) { + TPixelGR8 *srcPix = grayAppImage->pixels(sy); + TPixelCM32 *tarPix = outImage->pixels(ty) + glyphOrig.x; + for (int x = 0; x < srcLx; ++x) { + int tone = 256 - (srcPix->value << 2); + + // grayScale ToonzImage tone Meaning + // 0 255 Bg = PurePaint + // 1 252 + // ... + // 63 4 + // 64 0 Fg = Pure Ink + + if (tone < 0) + tone = 0; + + if (tone >= 255) + *tarPix = bgColor; + else + *tarPix = TPixelCM32(inkId, 0, tone); // BackgroundStyle,tone); + + ++srcPix; + ++tarPix; + } + } + grayAppImage->unlock(); + outImage->unlock(); + + return getDistance(charcode, nextCharCode); +} + +//----------------------------------------------------------------------------- + +TPoint TFont::getDistance(wchar_t firstChar, wchar_t secondChar) const +{ + int advance; + BOOL result = GetCharWidth32W(m_pimpl->m_hdc, firstChar, firstChar, &advance); + assert(result); + if (m_pimpl->m_hasKerning && secondChar) { + KerningPairs::iterator it = m_pimpl->m_kerningPairs.find(make_pair(firstChar, secondChar)); + if (it != m_pimpl->m_kerningPairs.end()) { + advance += it->second; + } + } + return TPoint(advance, 0); +} + +//----------------------------------------------------------------------------- + +int TFont::getMaxHeight() const +{ + return m_pimpl->m_metrics.tmHeight; +} + +//----------------------------------------------------------------------------- + +int TFont::getMaxWidth() const +{ + return m_pimpl->m_metrics.tmMaxCharWidth; +} +//----------------------------------------------------------------------------- + +int TFont::getLineAscender() const +{ + return m_pimpl->m_metrics.tmAscent; +} + +//----------------------------------------------------------------------------- + +int TFont::getLineDescender() const +{ + return -m_pimpl->m_metrics.tmDescent; +} + +//----------------------------------------------------------------------------- + +bool TFont::hasKerning() const +{ + return m_pimpl->m_hasKerning; +} + +//----------------------------------------------------------------------------- + +bool TFont::hasVertical() const +{ + return m_pimpl->m_hasVertical; +} + +//============================================================================= +//==================== TFontManager ===================================== +//============================================================================= + +//--------------------------------------------------------- + +struct TFontManager::Impl { + WindowsFontTable m_families; + bool m_loaded; + + LOGFONTW m_currentLogFont; + TFont *m_currentFont; + + // this option is set by library user when he wants to write vertically. + // In this implementation, if m_vertical is true and the font + // has the @-version, the library use it. + bool m_vertical; + + TFontManager::Impl() + : m_loaded(false), m_currentFont(0), m_vertical(false) + { + } +}; + +//--------------------------------------------------------- + +TFontManager::TFontManager() +{ + m_pimpl = new TFontManager::Impl(); +} + +//--------------------------------------------------------- + +TFontManager::~TFontManager() +{ + delete m_pimpl; +} + +//--------------------------------------------------------- + +TFontManager *TFontManager::instance() +{ + static TFontManager theManager; + return &theManager; +} + +//--------------------------------------------------------- + +namespace +{ +BOOL CALLBACK EnumFamCallBack( + CONST LOGFONTW *lplf, + CONST TEXTMETRICW *, + DWORD FontType, + LPARAM data) +{ + if (FontType & TRUETYPE_FONTTYPE) { + LOGFONTW newLplf = *lplf; + newLplf.lfHeight = 200; + newLplf.lfWidth = 0; + WindowsFontTable &table = *(WindowsFontTable *)data; + table[lplf->lfFaceName] = newLplf; + return TRUE; + } + return TRUE; +} +} + +//--------------------------------------------------------- + +void TFontManager::loadFontNames() +{ + if (m_pimpl->m_loaded) + return; + + HDC hdc = CreateCompatibleDC(NULL); + if (!hdc) + throw TFontLibraryLoadingError(); + EnumFontFamiliesW(hdc, + (LPCWSTR)NULL, + (FONTENUMPROCW)EnumFamCallBack, + (LPARAM) & (m_pimpl->m_families)); + DeleteDC(hdc); + hdc = 0; + if (m_pimpl->m_families.empty()) + throw TFontLibraryLoadingError(); + + m_pimpl->m_loaded = true; +} + +//--------------------------------------------------------- + +void TFontManager::setFamily(const wstring family) +{ + + wstring userFamilyName = ((family.c_str())[0] == L'@') ? wstring(family.c_str() + 1) : family; + wstring realFamilyName = (m_pimpl->m_vertical && (family.c_str())[0] != L'@') ? L"@" + family : family; + + wstring currentFamilyName = wstring(m_pimpl->m_currentLogFont.lfFaceName); + + if (currentFamilyName == realFamilyName) + return; + + LOGFONTW logfont; + if (m_pimpl->m_vertical) { + WindowsFontTable::iterator it = m_pimpl->m_families.find(realFamilyName); + if (it != m_pimpl->m_families.end()) + logfont = it->second; + else { + it = m_pimpl->m_families.find(userFamilyName); + assert(it != m_pimpl->m_families.end()); + if (it != m_pimpl->m_families.end()) + logfont = it->second; + else + throw TFontCreationError(); + } + } else { + WindowsFontTable::iterator it = m_pimpl->m_families.find(userFamilyName); + assert(it != m_pimpl->m_families.end()); + if (it != m_pimpl->m_families.end()) + logfont = it->second; + else + throw TFontCreationError(); + } + + if (m_pimpl->m_currentFont) { + logfont.lfHeight = m_pimpl->m_currentLogFont.lfHeight; + logfont.lfItalic = m_pimpl->m_currentLogFont.lfItalic; + logfont.lfWeight = m_pimpl->m_currentLogFont.lfWeight; + } else + logfont.lfHeight = 200; + + try { + HDC hdc = CreateCompatibleDC(NULL); + TFont *newfont = new TFont(logfont, hdc); + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = newfont; + m_pimpl->m_currentLogFont = logfont; + } catch (TException &) { + throw TFontCreationError(); + } +} + +//--------------------------------------------------------- + +void TFontManager::setTypeface(const wstring typeface) +{ + LOGFONTW logfont = m_pimpl->m_currentLogFont; + logfont.lfItalic = (typeface == L"Italic" || + typeface == L"Bold Italic") + ? TRUE + : FALSE; + logfont.lfWeight = (typeface == L"Bold" || + typeface == L"Bold Italic") + ? 700 + : 400; + + try { + HDC hdc = CreateCompatibleDC(NULL); + TFont *newfont = new TFont(logfont, hdc); + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = newfont; + m_pimpl->m_currentLogFont = logfont; + } catch (TException &) { + throw TFontCreationError(); + } +} + +//--------------------------------------------------------- + +void TFontManager::setSize(int size) +{ + LOGFONTW logfont = m_pimpl->m_currentLogFont; + logfont.lfHeight = size; + try { + HDC hdc = CreateCompatibleDC(NULL); + TFont *newfont = new TFont(logfont, hdc); + delete m_pimpl->m_currentFont; + m_pimpl->m_currentFont = newfont; + m_pimpl->m_currentLogFont = logfont; + } catch (TException &) { + throw TFontCreationError(); + } +} + +//--------------------------------------------------------- + +wstring TFontManager::getCurrentFamily() const +{ + wstring currentFamilyName = (m_pimpl->m_currentLogFont.lfFaceName[0] == L'@') ? wstring(m_pimpl->m_currentLogFont.lfFaceName + 1) : wstring(m_pimpl->m_currentLogFont.lfFaceName); + return currentFamilyName; +} + +//--------------------------------------------------------- + +wstring TFontManager::getCurrentTypeface() const +{ + if (m_pimpl->m_currentLogFont.lfItalic) { + if (m_pimpl->m_currentLogFont.lfWeight == 700) + return L"Bold Italic"; + else + return L"Italic"; + } else { + if (m_pimpl->m_currentLogFont.lfWeight == 700) + return L"Bold"; + else + return L"Regular"; + } +} + +//--------------------------------------------------------- + +TFont *TFontManager::getCurrentFont() +{ + if (m_pimpl->m_currentFont) + return m_pimpl->m_currentFont; + + if (!m_pimpl->m_currentFont) + loadFontNames(); + + assert(!m_pimpl->m_families.empty()); + setFamily(m_pimpl->m_families.begin()->first); + + return m_pimpl->m_currentFont; +} + +//--------------------------------------------------------- + +void TFontManager::getAllFamilies(vector &families) const +{ + families.clear(); + families.reserve(m_pimpl->m_families.size()); + WindowsFontTable::iterator it = m_pimpl->m_families.begin(); + for (; it != m_pimpl->m_families.end(); ++it) { + if ((it->first)[0] != L'@') + families.push_back(it->first); + } +} + +//--------------------------------------------------------- + +void TFontManager::getAllTypefaces(vector &typefaces) const +{ + typefaces.resize(4); + typefaces[0] = L"Regular"; + typefaces[1] = L"Italic"; + typefaces[2] = L"Bold"; + typefaces[3] = L"Bold Italic"; +} + +//--------------------------------------------------------- + +void TFontManager::setVertical(bool vertical) +{ + if (m_pimpl->m_vertical == vertical) + return; + m_pimpl->m_vertical = vertical; + + wstring currentFamilyName = (m_pimpl->m_currentLogFont.lfFaceName[0] == L'@') ? wstring(m_pimpl->m_currentLogFont.lfFaceName + 1) : wstring(m_pimpl->m_currentLogFont.lfFaceName); + if (vertical) + currentFamilyName = L'@' + currentFamilyName; + setFamily(currentFamilyName); +} diff --git a/toonz/sources/common/tvrender/tfont_proxy.cpp b/toonz/sources/common/tvrender/tfont_proxy.cpp new file mode 100644 index 0000000..b2727cb --- /dev/null +++ b/toonz/sources/common/tvrender/tfont_proxy.cpp @@ -0,0 +1,432 @@ + + +#ifdef __LP64__ + +//Toonz includes +#include "tvectorimage.h" +#include "tstroke.h" +#include "trop.h" + +//Qt includes +#include + +/* +なぜか tipc.h の + + template + QDataStream& operator>>(QDataStream& ds, std::vector& vec) + +が T=TThickPoint でインスタンス化されようとして曖昧ですエラーになる +*/ +//tipc includes +#include "tipc.h" +#include "t32bitsrv_wrap.h" + +#include "tfont.h" + +//************************************************************************** +// Local namespace stuff +//************************************************************************** + +namespace +{ + +QDataStream &operator>>(QDataStream &ds, TThickPoint &p) +{ + return ds >> p.x >> p.y >> p.thick; +} + +void readVImage(TVectorImageP &vi, tipc::Message &msg) +{ + std::vector strokeCPs; + + //Read in all strokes + while (!msg.ds().atEnd()) { + strokeCPs.clear(); + msg >> strokeCPs; + + vi->addStroke(new TStroke(strokeCPs)); + } + + vi->group(0, vi->getStrokeCount()); +} + +} //namespace + +//************************************************************************** +// TFontManager Private stuff +//************************************************************************** + +/* + The proxied Impl private just caches some data of the actual + background counterpart +*/ +struct TFontManager::Impl { + int m_ascender; + int m_descender; + + Impl() {} + ~Impl() {} +}; + +//************************************************************************** +// TFontManager Proxied implementation +//************************************************************************** + +TFontManager::TFontManager() +{ + m_pimpl = new TFontManager::Impl(); +} + +//-------------------------------------------------------------- + +TFontManager::~TFontManager() +{ + delete m_pimpl; +} + +//-------------------------------------------------------------- + +TFontManager *TFontManager::instance() +{ + static TFontManager theInstance; + return &theInstance; +} + +//-------------------------------------------------------------- + +//!This function is retained from old 32-bit legacy code. +//!Its use is now forbidden - use the TFontManager directly instead. +TFont *TFontManager::getCurrentFont() +{ + assert(false); + return 0; +} + +//-------------------------------------------------------------- + +//!\note Throws TFontLibraryLoadingError if fonts could not be loaded +void TFontManager::loadFontNames() +{ + //Connect to the 32-bit background server process + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTloadFontNames")); + if (tipc::readMessage(stream, msg) != "ok") + throw TFontLibraryLoadingError(); +} + +//-------------------------------------------------------------- + +//!\note Throws TFontCreationError if the font could not be created, and +//!leaves the old font as current. +void TFontManager::setFamily(const wstring family) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTsetFamily") << family); + if (tipc::readMessage(stream, msg) != "ok") + throw TFontCreationError(); +} + +//-------------------------------------------------------------- + +//!\note Throws TFontCreationError if the font could not be created, and +//!leaves the old font as current. +void TFontManager::setTypeface(const wstring typeface) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTsetTypeface") << typeface); + if (tipc::readMessage(stream, msg) != "ok") + throw TFontCreationError(); + + //Also, store the font's ascender and descender + msg >> m_pimpl->m_ascender >> m_pimpl->m_descender; +} + +//-------------------------------------------------------------- + +wstring TFontManager::getCurrentFamily() const +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTgetCurrentFamily")); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Could not get font family"); + + std::wstring family; + msg >> family; + return family; +} + +//-------------------------------------------------------------- + +wstring TFontManager::getCurrentTypeface() const +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTgetCurrentTypeface")); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Could not get font typeface"); + + std::wstring typeface; + msg >> typeface; + return typeface; +} + +//-------------------------------------------------------------- + +void TFontManager::getAllFamilies(vector &families) const +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTgetAllFamilies")); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Could not get font families"); + + msg >> families; +} + +//-------------------------------------------------------------- + +void TFontManager::getAllTypefaces(vector &typefaces) const +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTgetAllTypefaces")); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Could not get font typefaces"); + + msg >> typefaces; +} + +//-------------------------------------------------------------- + +void TFontManager::setVertical(bool vertical) +{ +} + +//-------------------------------------------------------------- + +void TFontManager::setSize(int size) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTsetSize") << size); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + + //Also, update ascender and descender + msg >> m_pimpl->m_ascender >> m_pimpl->m_descender; +} + +//-------------------------------------------------------------- + +TPoint TFontManager::getDistance(wchar_t firstChar, wchar_t secondChar) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTgetDistance") << firstChar << secondChar); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + + TPoint d; + msg >> d.x >> d.y; + return d; +} + +//-------------------------------------------------------------- + +int TFontManager::getMaxHeight() +{ + return m_pimpl->m_ascender - m_pimpl->m_descender; +} + +//-------------------------------------------------------------- + +int TFontManager::getMaxWidth() +{ + assert(!"not implemented yet"); + return 100; +} + +//-------------------------------------------------------------- + +bool TFontManager::hasVertical() +{ + return false; +} + +//-------------------------------------------------------------- + +bool TFontManager::hasKerning() +{ + return true; +} + +//-------------------------------------------------------------- + +int TFontManager::getLineAscender() +{ + return m_pimpl->m_ascender; +} + +//-------------------------------------------------------------- + +int TFontManager::getLineDescender() +{ + return m_pimpl->m_descender; +} + +//-------------------------------------------------------------- + +TPoint TFontManager::drawChar(TVectorImageP &outImage, wchar_t charcode, wchar_t nextCode) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + stream << (msg << QString("$FNTdrawCharVI") << charcode << nextCode); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + + TPoint ret; + msg >> ret.x >> ret.y; + ::readVImage(outImage, msg); + + return ret; +} + +//-------------------------------------------------------------- + +TPoint TFontManager::drawChar(TRasterGR8P &outImage, TPoint &glyphOrigin, wchar_t charcode, wchar_t nextCode) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + QString shMemId(tipc::uniqueId()), res; + { + //Invoke the appropriate command + stream << (msg << QString("$FNTdrawCharGR") + << shMemId << charcode << nextCode); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + } + + TDimension dim(0, 0); + msg >> dim.lx >> dim.ly; + TPoint ret; + msg >> ret.x >> ret.y; + + //Create outImage + outImage = TRasterGR8P(dim.lx, dim.ly); + + QSharedMemory shmem(shMemId); + shmem.attach(); + shmem.lock(); + + //Copy the returned image to outImage + TRasterGR8P ras(dim.lx, dim.ly, dim.lx, (TPixelGR8 *)shmem.data()); + TRop::copy(outImage, ras); + + shmem.unlock(); + shmem.detach(); + + //Release the shared segment + stream << (msg << tipc::clr << QString("$shmem_release") << shMemId); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + + return ret; +} + +//-------------------------------------------------------------- + +TPoint TFontManager::drawChar(TRasterCM32P &outImage, TPoint &glyphOrigin, + int inkId, wchar_t charcode, wchar_t nextCode) +{ + QLocalSocket socket; + tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), -1, t32bitsrv::srvCmdline()); + + tipc::Stream stream(&socket); + tipc::Message msg; + + QString shMemId(tipc::uniqueId()), res; + { + //Invoke the appropriate command + stream << (msg << QString("$FNTdrawCharCM") + << inkId << shMemId << charcode << nextCode); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + } + + TDimension dim(0, 0); + msg >> dim.lx >> dim.ly; + TPoint ret; + msg >> ret.x >> ret.y; + + //Create outImage + outImage = TRasterCM32P(dim.lx, dim.ly); + + QSharedMemory shmem(shMemId); + shmem.attach(); + shmem.lock(); + + //Copy the returned image to outImage + TRasterCM32P ras(dim.lx, dim.ly, dim.lx, (TPixelCM32 *)shmem.data()); + TRop::copy(outImage, ras); + + shmem.unlock(); + shmem.detach(); + + //Release the shared segment + stream << (msg << tipc::clr << QString("$shmem_release") << shMemId); + if (tipc::readMessage(stream, msg) != "ok") + throw TException("Unexpected error"); + + return ret; +} + +#endif diff --git a/toonz/sources/common/tvrender/tglcurves.cpp b/toonz/sources/common/tvrender/tglcurves.cpp new file mode 100644 index 0000000..83cc834 --- /dev/null +++ b/toonz/sources/common/tvrender/tglcurves.cpp @@ -0,0 +1,55 @@ + + +#include "tgl.h" +//#include "tvectorgl.h" +//#include "tstroke.h" +#include "tstrokeprop.h" +//#include "tpalette.h" +#include "tvectorrenderdata.h" +//#include "tpalette.h" +//#include "tcolorstyles.h" + +//============================================================================= +#ifdef SPOSTATOINTGLREGIONS +void tglDraw(const TVectorRenderData &rd, const TStroke *s) +{ + assert(s); + if (!s) + return; + + const TStroke &stroke = *s; + + // initialize information for aaliasing + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glEnable(GL_LINE_SMOOTH); + + // it's necessary to do line function of pixel size ? + glLineWidth((float)1.0); + + assert(rd.m_palette); + TStrokeProp *prop = stroke.getProp(/*rd.m_palette*/); + /////questo codice stava dentro tstroke::getprop///////// + TColorStyle *style = rd.m_palette->getStyle(stroke->getStyle() /*m_imp->m_styleId*/); + if (!style->isStrokeStyle() || style->isEnabled() == false) { + prop = 0; + } else { + if (!prop || style != prop->getColorStyle()) { + stroke->setProp(style->makeStrokeProp(stroke)); + prop = stroke->getProp(); + } + } + /// + //--------------------- + if (prop) + prop->draw(rd); + //--------------------- + + tglColor(TPixel32::White); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} +#endif +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tvrender/tglregions.cpp b/toonz/sources/common/tvrender/tglregions.cpp new file mode 100644 index 0000000..5ef8493 --- /dev/null +++ b/toonz/sources/common/tvrender/tglregions.cpp @@ -0,0 +1,607 @@ + + +#include "tgl.h" +#include "tvectorgl.h" +#include "tregion.h" +#include "tregionprop.h" +#include "tstrokeutil.h" +#include "tvectorrenderdata.h" +#include "tvectorimage.h" +#include "tpalette.h" +#include "tcolorfunctions.h" +#include "tsimplecolorstyles.h" +#include "tthreadmessage.h" +#include "tstrokeprop.h" + +#include "tconvert.h" +#include "tcurves.h" +#include "tstrokeoutline.h" + +#ifndef WIN32 +#define CALLBACK +#endif + +#ifndef checkErrorsByGL +#define checkErrorsByGL \ + { \ + GLenum err = glGetError(); \ + assert(err != GL_INVALID_ENUM); \ + assert(err != GL_INVALID_VALUE); \ + assert(err != GL_INVALID_OPERATION); \ + assert(err != GL_STACK_OVERFLOW); \ + assert(err != GL_STACK_UNDERFLOW); \ + assert(err != GL_OUT_OF_MEMORY); \ + assert(err == GL_NO_ERROR); \ + } +#endif + +#undef checkErrorsByGL +#define checkErrorsByGL + +using namespace std; + +//----------------------------------------------------------------------------- +/* +DV_EXPORT_API void mylog(std::string s) +{ + static TThread::Mutex mutex; + QMutexLocker sl(mutex); + std::ofstream os("C:\\gmt\\buttami\\bu.txt", std::ios::app); + + LARGE_INTEGER ticksPerSecond; + LARGE_INTEGER tick; + static LARGE_INTEGER firstTick; + static bool firstTime = true; + long dt = 0; + + QueryPerformanceFrequency(&ticksPerSecond); + QueryPerformanceCounter(&tick); + if(firstTime) {firstTick = tick;firstTime=false;} + else + { + dt = (long)(1000000*(tick.QuadPart-firstTick.QuadPart)/ticksPerSecond.QuadPart); + } + os << dt << ":" << s << std::endl; +} +*/ + +//============================================================================= +#ifdef _DEBUG + +bool checkQuadraticDistance(TStroke *stroke, bool checkThickness) +{ + UINT i, qCount = stroke->getChunkCount(); + const TThickQuadratic *q; + TThickPoint p1, p2, p3; + + //se i punti coincidono e' una stroke puntiforme ed e' ammessa + if (qCount == 1) + return true; + + for (i = 0; i != qCount; i++) { + q = stroke->getChunk(i); + p1 = q->getThickP0(); + p2 = q->getThickP1(); + p3 = q->getThickP2(); + + if (areAlmostEqual(p1.x, p2.x) && areAlmostEqual(p2.x, p3.x) && + areAlmostEqual(p1.y, p2.y) && areAlmostEqual(p2.y, p3.y) && + (!checkThickness || + (areAlmostEqual(p1.thick, p2.thick) && areAlmostEqual(p2.thick, p3.thick)))) + return false; + } + return true; +} + +//----------------------------------------------------------------------------- + +void drawControlPoints(const TVectorRenderData &rd, TStroke *stroke, double pixelSize, bool allPoints = true) +{ + int i; + TPointD p; + glPushMatrix(); + + tglMultMatrix(rd.m_aff); + + glPointSize(2.0); + glBegin(GL_POINTS); + + if (allPoints) { + int n = stroke->getControlPointCount(); + for (i = 0; i < n; ++i) { + p = stroke->getControlPoint(i); + glColor3d((i + 1) & 1, i & 1, 0.0); + glVertex2d(p.x, p.y); + } + } else { + int n = stroke->getChunkCount(); + for (i = 0; i < n; ++i) { + const TThickQuadratic *chunk = stroke->getChunk(i); + p = chunk->getP0(); + glColor3d(1.0, 0.0, 0.0); + glVertex2d(p.x, p.y); + } + const TThickQuadratic *chunk = stroke->getChunk(n - 1); + glColor3d(1.0, 0.0, 0.0); + p = chunk->getP2(); + glVertex2d(p.x, p.y); + } + + glEnd(); + glPopMatrix(); +} + +#endif + +//============================================================================= + +/*TPixel TransparencyCheckBlackBgInk = TPixel(255,255,255); +TPixel TransparencyCheckWhiteBgInk = TPixel(0,0,0); +TPixel TransparencyCheckPaint = TPixel(127,127,127);*/ +static int Index = 0; +void tglDraw(const TVectorRenderData &rd, TRegion *r, bool pushAttribs) +{ + checkErrorsByGL; + assert(r); + checkErrorsByGL; + if (!r) + return; + bool alphaChannel = rd.m_alphaChannel; + checkErrorsByGL; + + int j = 0; + bool visible = false; + int colorCount = 0; + + TColorStyleP style; + if (rd.m_paintCheckEnabled && r->getStyle() == rd.m_colorCheckIndex) { + static TSolidColorStyle *redColor = new TSolidColorStyle(); + redColor->addRef(); + redColor->setMainColor(TPixel::Red); + style = redColor; + } else if (rd.m_tcheckEnabled) { + static TSolidColorStyle *color = new TSolidColorStyle(); + color->addRef(); + color->setMainColor(rd.m_tCheckPaint); + style = color; + } else + style = rd.m_palette->getStyle(r->getStyle()); + + colorCount = style->getColorParamCount(); + if (colorCount == 0) { //for example texture + visible = true; + } else { + visible = false; + for (j = 0; j < colorCount && !visible; j++) { + TPixel32 color = style->getColorParamValue(j); + if (rd.m_cf) + color = (*(rd.m_cf))(color); + if (color.m != 0) + visible = true; + } + } + if (visible) { + TRegionProp *prop = r->getProp(/*rd.m_palette*/); + ///questo codice satva dentro tregion::getprop///// + int styleId = r->getStyle(); + if (styleId) { + // TColorStyle * style = rd.m_palette->getStyle(styleId); + if (!style->isRegionStyle() || style->isEnabled() == false) { + prop = 0; + } else { + //Warning: The same remark of stroke props holds here. + if (!prop || style.getPointer() != prop->getColorStyle()) { + r->setProp(style->makeRegionProp(r)); + prop = r->getProp(); + } + } + } + + ////// draw + if (prop) { + if (pushAttribs) + glPushAttrib(GL_ALL_ATTRIB_BITS); + + tglEnableLineSmooth(true); +//#define DRAW_EDGE_NUMBERS +#ifdef DRAW_EDGE_NUMBERS + glPushMatrix(); + tglMultMatrix(rd.m_aff); + switch (Index % 7) { + case 0: + tglColor(TPixel::Red); + break; + case 1: + tglColor(TPixel::Green); + break; + case 2: + tglColor(TPixel::Blue); + break; + case 3: + tglColor(TPixel::Cyan); + break; + case 4: + tglColor(TPixel::Magenta); + break; + case 5: + tglColor(TPixel::Yellow); + break; + case 6: + tglColor(TPixel::Black); + break; + default: + tglColor(TPixel::Red); + break; + } + + Index++; + if (rIndex == 2) { + double y = r->getEdge(0)->m_s->getThickPoint((r->getEdge(0)->m_w0 + r->getEdge(0)->m_w1) / 2.0).y; + tglDrawSegment(TPointD(-1000, y), TPointD(1000, y)); + } + + for (int i = 0; i < (int)r->getEdgeCount(); i++) { + TEdge *e = r->getEdge(i); + TPointD p = e->m_s->getPoint(0.8 * e->m_w0 + 0.2 * e->m_w1); + if (i == 0) + tglDrawText(p, (QString::number(rIndex) + QString("-0")).toStdString()); + else + tglDrawText(p, QString::number(i).toStdString()); + if (e->m_index == 3) { + tglColor(TPixel::Black); + TStroke *s = e->m_s; + drawPoint(s->getChunk(0)->getP0(), .3); + tglColor(TPixel::Red); + tglDrawText(s->getChunk(0)->getP0(), QString::number(0).toStdString()); + for (int ii = 0; ii < s->getChunkCount(); ii++) { + drawPoint(s->getChunk(ii)->getP2(), .3); + if (ii < s->getChunkCount() - 1) { + tglColor(TPixel::Red); + tglDrawText(s->getChunk(ii)->getP2(), QString::number(ii + 1).toStdString()); + } + } + } + } + glPopMatrix(); +#endif + + if (alphaChannel) { + GLboolean red, green, blue, alpha; + tglGetColorMask(red, green, blue, alpha); + + //Draw RGB channels + tglEnableBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(red, green, blue, GL_FALSE); + prop->draw(rd); + + //Draw Matte channel + tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, alpha); + prop->draw(rd); + + glColorMask(red, green, blue, alpha); + } else { + //pezza: in render, le aree fillate dei custom styles sparivano. + if (!rd.m_isOfflineRender || !rd.m_isImagePattern) + tglRgbOnlyColorMask(); //RGB components only + + prop->draw(rd); + } + + if (pushAttribs) + glPopAttrib(); + } + } + + for (UINT i = 0; i < r->getSubregionCount(); i++) + tglDraw(rd, r->getSubregion(i), pushAttribs); + checkErrorsByGL; +} + +//----------------------------------------------------------------------------- + +void tglDrawMask(const TVectorRenderData &rd1, const TVectorImage *vim) +{ + UINT i; + assert(vim); + if (!vim) + return; + TVectorRenderData rd(rd1); + + glPushAttrib(GL_ALL_ATTRIB_BITS); + + if (!rd.m_palette) { + TPalette *vPalette = vim->getPalette(); + assert(vPalette); + rd.m_palette = vPalette; + } + for (i = 0; i < vim->getRegionCount(); i++) + tglDraw(rd, vim->getRegion(i), false); + + glPopAttrib(); +} + +//----------------------------------------------------------------------------- + +namespace +{ +bool isOThick(const TStroke *s) +{ + int i; + for (i = 0; i < s->getControlPointCount(); i++) + if (s->getControlPoint(i).thick != 0) + return false; + return true; +} +} + +void tglDraw(const TVectorRenderData &rd, const TStroke *s, bool pushAttribs) +{ + assert(s); + if (!s) + return; + + TStrokeProp *prop = 0; + bool pushedAttribs = false; + + try { + TColorStyleP style; + TStroke *stroke = const_cast(s); + if (rd.m_inkCheckEnabled && s->getStyle() == rd.m_colorCheckIndex) { + static TSolidColorStyle *redColor = new TSolidColorStyle(); + redColor->addRef(); + redColor->setMainColor(TPixel::Red); + style = redColor; + } else if (rd.m_tcheckEnabled) { + static TSolidColorStyle *color = new TSolidColorStyle(); + color->addRef(); + color->setMainColor(rd.m_tCheckInk); + style = color; + } else + style = rd.m_palette->getStyle(stroke->getStyle()); + + if (!rd.m_show0ThickStrokes && isOThick(s) && dynamic_cast(style.getPointer()) // This is probably to exclude TCenterlineStrokeStyle-like styles + && !rd.m_tcheckEnabled) // I wonder why this? + return; + + // const TStroke& stroke = *s; //serve??? + + assert(rd.m_palette); + + prop = s->getProp(/*rd.m_palette*/); + /////questo codice stava dentro tstroke::getprop///////// + if (prop) + prop->getMutex()->lock(); + + if (!style->isStrokeStyle() || style->isEnabled() == false) { + if (prop) + prop->getMutex()->unlock(); + + prop = 0; + } else { + //Warning: the following pointers check is conceptually wrong - we + //keep it because the props maintain SMART POINTER-like reference to + //the associated style. This prevents the style from being destroyed + //while still referenced by the prop. + if (!prop || style.getPointer() != prop->getColorStyle()) { + if (prop) + prop->getMutex()->unlock(); + + stroke->setProp(style->makeStrokeProp(stroke)); + prop = stroke->getProp(); + if (prop) + prop->getMutex()->lock(); + } + } + + //--------- draw ------------ + if (!prop) + return; + + if (pushAttribs) + glPushAttrib(GL_ALL_ATTRIB_BITS), pushedAttribs = true; + + bool alphaChannel = rd.m_alphaChannel, antialias = rd.m_antiAliasing; + TVectorImagePatternStrokeProp *aux = dynamic_cast(prop); + if (aux) //gli image pattern vettoriali tornano in questa funzione....non facendo il corpo dell'else'si evita di disegnarli due volte! + prop->draw(rd); + else { + if (antialias) + tglEnableLineSmooth(true); + else + tglEnableLineSmooth(false); + + if (alphaChannel) { + GLboolean red, green, blue, alpha; + tglGetColorMask(red, green, blue, alpha); + + //Draw RGB channels + tglEnableBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(red, green, blue, GL_FALSE); + prop->draw(rd); + + //Draw Matte channel + tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, alpha); + prop->draw(rd); + + glColorMask(red, green, blue, alpha); + } else { + tglEnableBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + prop->draw(rd); + } + } + + if (pushAttribs) + glPopAttrib(), pushedAttribs = false; + + prop->getMutex()->unlock(); + //--------------------- + } catch (...) { + if (prop) + prop->getMutex()->unlock(); + if (pushedAttribs) + glPopAttrib(); + } +} + +//------------------------------------------------------------------------------------ + +void tglDoDraw(const TVectorRenderData &rd, TRegion *r) +{ + bool visible = false; + int colorCount = 0; + if (!r) + return; + + TColorStyleP style = rd.m_palette->getStyle(r->getStyle()); + colorCount = style->getColorParamCount(); + if (colorCount == 0) //for example texture + visible = true; + else { + visible = false; + for (int j = 0; j < colorCount && !visible; j++) { + TPixel32 color = style->getColorParamValue(j); + if (rd.m_cf) + color = (*(rd.m_cf))(color); + if (color.m != 0) + visible = true; + } + } + if (visible) + tglDraw(rd, r, false); + else + for (UINT j = 0; j < r->getSubregionCount(); j++) + tglDraw(rd, r->getSubregion(j), false); +} + +//------------------------------------------------------------------------------------ + +void tglDoDraw(const TVectorRenderData &rd, const TStroke *s) +{ + bool visible = false; + int colorCount = 0; + + const TPalette *palette = rd.m_palette; + + int styleId = s->getStyle(); + //assert(0<=styleId && styleIdgetStyle(styleId); + assert(style); + colorCount = style->getColorParamCount(); + if (colorCount == 0) + visible = true; + else { + visible = false; + for (int j = 0; j < style->getColorParamCount() && !visible; j++) { + TPixel32 color = style->getColorParamValue(j); + if (rd.m_cf) + color = (*(rd.m_cf))(color); + if (color.m != 0) + visible = true; + } + } +#if DISEGNO_OUTLINE == 0 + if (visible) +#endif + tglDraw(rd, s, false); + +#ifdef _DEBUG +//drawControlPoints(rd, vim->getStroke(i), sqrt(tglGetPixelSize2()), true); +//assert(checkQuadraticDistance(vim->getStroke(i),true)); +#endif +} + +//------------------------------------------------------------------------------------ + +namespace +{ + +void doDraw(const TVectorImage *vim, const TVectorRenderData &_rd, bool drawEnteredGroup) +{ + static TOnionFader *fade = new TOnionFader(TPixel::White, 0.5); + + TVectorRenderData rd(_rd); + + if (!rd.m_palette) { + TPalette *vPalette = vim->getPalette(); + rd.m_palette = vPalette; + if (!vPalette) + return; + } + + if (!drawEnteredGroup && !rd.m_isIcon && vim->isInsideGroup() > 0) + rd.m_cf = fade; + + TVectorRenderData rdRegions = rd; + + /*if (rd.m_drawRegions && rd.m_isImagePattern)//gli image pattern hanno bisogno dell'antialiasig per le linee, ma sulle aree ci sarebbero un sacco di assert + rdRegions.m_alphaChannel = rdRegions.m_antiAliasing = false;*/ + UINT strokeIndex = 0; + Index = 0; + + while (strokeIndex < vim->getStrokeCount()) // ogni ciclo di while disegna un gruppo + { + int currStrokeIndex = strokeIndex; + if (!rd.m_isIcon && vim->isInsideGroup() > 0 && + ((drawEnteredGroup && !vim->isEnteredGroupStroke(strokeIndex)) || + !drawEnteredGroup && vim->isEnteredGroupStroke(strokeIndex))) { + while (strokeIndex < vim->getStrokeCount() && vim->sameGroup(strokeIndex, currStrokeIndex)) + strokeIndex++; + continue; + } + + if (rd.m_drawRegions) + for (UINT regionIndex = 0; regionIndex < vim->getRegionCount(); regionIndex++) + if (vim->sameGroupStrokeAndRegion(currStrokeIndex, regionIndex)) + tglDoDraw(rdRegions, vim->getRegion(regionIndex)); + while (strokeIndex < vim->getStrokeCount() && vim->sameGroup(strokeIndex, currStrokeIndex)) { +#if DISEGNO_OUTLINE == 1 + CurrStrokeIndex = strokeIndex; + CurrVimg = vim; +#endif + tglDoDraw(rd, vim->getStroke(strokeIndex)); + strokeIndex++; + } + } +} +} + +//------------------------------------------------------------------------------------ + +void tglDraw(const TVectorRenderData &rd, const TVectorImage *vim) +{ + assert(vim); + if (!vim) + return; + + QMutexLocker sl(vim->getMutex()); + + checkErrorsByGL; + checkErrorsByGL; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + + //if(!rd.m_palette) rd.m_palette = vim->getPalette(); + //mylog("tglDraw start; mutex=" + toString((unsigned long)&vim->getMutex())); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); + + doDraw(vim, rd, false); + if (!rd.m_isIcon && vim->isInsideGroup() > 0) + doDraw(vim, rd, true); + + glDisable(GL_ALPHA_TEST); + + glPopAttrib(); + +#ifdef _DEBUG + vim->drawAutocloses(rd); +#endif + checkErrorsByGL; + //mylog("tglDraw stop"); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tvrender/tinbetween.cpp b/toonz/sources/common/tvrender/tinbetween.cpp new file mode 100644 index 0000000..82e6ec0 --- /dev/null +++ b/toonz/sources/common/tvrender/tinbetween.cpp @@ -0,0 +1,1517 @@ + + +#include "tinbetween.h" +#include "tcurves.h" +#include "tstroke.h" +#include "tpalette.h" +//#include "tcolorstyles.h" +//#include "tregion.h" +#include "tmathutil.h" +//#include "tstrokeutil.h" +//#include "tsystem.h" +#include +#include +#include + +#include "../tvectorimage/tvectorimageP.h" + +//#include "tdebugmessage.h" +//-------------------------------------------------------------------------------------- + +double average(std::vector &values, double range = 2.5) +{ + UINT size = values.size(); + if (size == 0) + return std::numeric_limits::signaling_NaN(); + + if (size == 1) + return values[0]; + + double sum = 0; + UINT j = 0; + for (; j < size; j++) { + sum += values[j]; + } + + double average = sum / size; + + double variance = 0; + for (j = 0; j < size; j++) { + variance += (average - values[j]) * (average - values[j]); + } + variance /= size; + + double err; + int acceptedNum = 0; + sum = 0; + for (j = 0; j < size; j++) { + err = values[j] - average; + err *= err; + if (err <= range * variance) { + sum += values[j]; + acceptedNum++; + } + } + + assert(acceptedNum > 0); + return (acceptedNum > 0) ? sum / (double)acceptedNum : average; +} + +//-------------------------------------------------------------------------------------- + +double weightedAverage(std::vector &values, std::vector &weights, double range = 2.5) +{ + UINT size = values.size(); + if (size == 0) + return std::numeric_limits::signaling_NaN(); + + double sum = 0; + double totalWeight = 0; + UINT j = 0; + for (; j < size; j++) { + sum += weights[j] * values[j]; + totalWeight += weights[j]; + } + + assert(totalWeight > 0); + if (totalWeight == 0) + return std::numeric_limits::signaling_NaN(); + + double average = sum / totalWeight; + + double variance = 0; + for (j = 0; j < size; j++) { + variance += weights[j] * (average - values[j]) * (average - values[j]); + } + variance /= totalWeight; + + double err; + totalWeight = 0; + sum = 0; + for (j = 0; j < size; j++) { + err = values[j] - average; + err *= err; + if (err <= range * variance) { + sum += weights[j] * values[j]; + totalWeight += weights[j]; + } + } + + assert(totalWeight > 0); + return (totalWeight != 0) ? sum / totalWeight : average; +} + +//-------------------------------------------------------------------------------------- + +inline UINT angleNumber(const std::vector> &corners, double angle) +{ + UINT count = 0; + UINT size = corners.size(); + for (UINT j = 0; j < size; j++) + if (corners[j].second >= angle) + count++; + + return count; +} + +//-------------------------------------------------------------------------------------- + +inline bool isTooComplex(UINT n1, UINT n2, UINT maxSubsetNumber = 100) +{ + UINT n, r; + if (n1 > n2) { + n = n1; + r = n2; + } else if (n1 < n2) { + n = n2; + r = n1; + } else { + assert(!"equal angle number"); + return false; + } + + if (n - r < r) + r = n - r; + + /* + + n*(n-1)* ...(n-r+1) < n^r that must be <= 2^(num bits of UINT)-1 + + s:=sizeof(UINT)*8 + + if + n <= 2^( (s-1)/r) => + + log n <= (s-1)/r (the base of log is 2) => + + r*log n <= s-1 => + + log n^r <= log 2^(s-1) => + + n^r <= 2^(s-1) < (2^s)-1 + + */ + + const UINT one = 1; + if (n > (one << ((sizeof(UINT) * 8 - 1) / r))) + return true; + + register UINT product1 = n; //product1 = n*(n-1)* ...(n-r+1) + for (register UINT i = 1; i < r; i++) + product1 *= --n; + + register UINT rFact = r; + + while (r > 1) + rFact *= --r; + + return (product1 / rFact > maxSubsetNumber); + //product1/rFact is number of combination + // ( n ) + // ( r ) +} + +//-------------------------------------------------------------------------------------- + +void eraseSmallAngles(std::vector> &corners, double angle) +{ + std::vector>::iterator it = corners.begin(); + + while (it != corners.end()) { + if ((*it).second < angle) + it = corners.erase(it); + else + ++it; + } +} + +//-------------------------------------------------------------------------------------- +//output: +// min is the minimum angle greater or equal to minDegree (i.e the minimum angle of the corners) +// max is tha maximum angle greater or equal to minDegree + +void detectCorners(const TStroke *stroke, double minDegree, + std::vector> &corners, double &min, double &max) +{ + const double minSin = fabs(sin(minDegree * TConsts::pi_180)); + double angle, vectorialProduct, metaCornerLen, partialLen; + + UINT quadCount1 = stroke->getChunkCount(); + TPointD speed1, speed2; + int j; + + TPointD tan1, tan2; + min = 180; + max = minDegree; + for (j = 1; j < (int)quadCount1; j++) { + speed1 = stroke->getChunk(j - 1)->getSpeed(1); + speed2 = stroke->getChunk(j)->getSpeed(0); + if (!(speed1 == TPointD() || speed2 == TPointD())) { + tan1 = normalize(speed1); + tan2 = normalize(speed2); + + vectorialProduct = fabs(cross(tan1, tan2)); + + if (tan1 * tan2 < 0) { + angle = 180 - asin(tcrop(vectorialProduct, -1.0, 1.0)) * TConsts::invOf_pi_180; + corners.push_back(std::make_pair(j, angle)); + + //------------------------------------------ + // TDebugMessage::getStream()< angle) + min = angle; + if (max < angle) + max = angle; + } else if (vectorialProduct >= minSin) { + angle = asin(tcrop(vectorialProduct, -1.0, 1.0)) * TConsts::invOf_pi_180; + corners.push_back(std::make_pair(j, angle)); + + //------------------------------------------ + // TDebugMessage::getStream()< angle) + min = angle; + if (max < angle) + max = angle; + } + } + } + + const double ratioLen = 2.5; + const double ratioAngle = 0.2; + std::vector>::iterator it = corners.begin(); + + for (j = 1; j < (int)quadCount1; j++) //"meta angoli" ( meta perche' derivabili) + { + if (it != corners.end() && j == (*it).first) { + ++it; + continue; + } + + if (j - 2 >= 0 && (corners.empty() || it == corners.begin() || j - 1 != (*(it - 1)).first) && j + 1 < (int)quadCount1 && (corners.empty() || it == corners.end() || j + 1 != (*it).first)) { + speed1 = stroke->getChunk(j - 2)->getSpeed(1); + speed2 = stroke->getChunk(j + 1)->getSpeed(0); + if (!(speed1 == TPointD() || speed2 == TPointD())) { + tan1 = normalize(speed1); + tan2 = normalize(speed2); + vectorialProduct = fabs(cross(tan1, tan2)); + + if (tan1 * tan2 < 0) { + angle = 180 - asin(tcrop(vectorialProduct, -1.0, 1.0)) * TConsts::invOf_pi_180; + + metaCornerLen = ratioLen * (stroke->getChunk(j - 1)->getLength() + stroke->getChunk(j)->getLength()); + partialLen = 0; + bool goodAngle = false; + + for (int i = j - 3; i >= 0 && (corners.empty() || it == corners.begin() || i + 1 != (*(it - 1)).first); i--) { + tan1 = stroke->getChunk(i)->getSpeed(1); + if (tan1 == TPointD()) + continue; + tan1 = normalize(tan1); + + tan2 = stroke->getChunk(i + 1)->getSpeed(1); + if (tan2 == TPointD()) + continue; + tan2 = normalize(tan2); + + vectorialProduct = fabs(cross(tan1, tan2)); + double nearAngle = asin(tcrop(vectorialProduct, -1.0, 1.0)) * TConsts::invOf_pi_180; + if (tan1 * tan2 < 0) + nearAngle = 180 - nearAngle; + + if (nearAngle > ratioAngle * angle) + break; + + partialLen += stroke->getChunk(i)->getLength(); + if (partialLen > metaCornerLen) { + goodAngle = true; + break; + } + } + if (goodAngle) { + partialLen = 0; + for (int i = j + 2; i + 1 < (int)quadCount1 && (corners.empty() || it == corners.end() || i + 1 != (*it).first); i++) { + tan1 = stroke->getChunk(i)->getSpeed(0); + if (tan1 == TPointD()) + continue; + tan1 = normalize(tan1); + + tan2 = stroke->getChunk(i + 1)->getSpeed(0); + if (tan2 == TPointD()) + continue; + tan2 = normalize(tan2); + + vectorialProduct = fabs(cross(tan1, tan2)); + double nearAngle = asin(tcrop(vectorialProduct, -1.0, 1.0)) * TConsts::invOf_pi_180; + if (tan1 * tan2 < 0) + nearAngle = 180 - nearAngle; + + if (nearAngle > 0.1 * angle) + break; + + partialLen += stroke->getChunk(i)->getLength(); + if (partialLen > metaCornerLen) { + goodAngle = true; + break; + } + } + } + + if (goodAngle) { + //l'angolo viene un po' declassato in quanto meta + it = corners.insert(it, std::make_pair(j, angle * 0.7)) + 1; + if (min > angle) + min = angle; + if (max < angle) + max = angle; + + // TDebugMessage::getStream()< &values) +{ + UINT size = values.size(); + if (size == 0) + return std::numeric_limits::signaling_NaN(); + + double sum = 0; + UINT j = 0; + for (; j < size; j++) { + sum += values[j]; + } + + double average = sum / size; + + double variance = 0; + for (j = 0; j < size; j++) { + variance += (average - values[j]) * (average - values[j]); + } + + return variance / size; +} + +//-------------------------------------------------------------------------------------- + +void findBestSolution(const TStroke *stroke1, const TStroke *stroke2, + std::pair *partialAngles1, UINT partialAngles1Size, + const std::vector> &angles2, + UINT r, std::list> &partialSolution, + double &bestValue, + std::vector &bestVector) +{ + //------------------------------------------------------------------- + if (r == partialAngles1Size) { + + UINT j; + vector> angles1; + + std::list>::iterator it = partialSolution.begin(); + + for (; it != partialSolution.end(); ++it) { + angles1.push_back(*it); + } + for (j = 0; j < partialAngles1Size; j++) { + angles1.push_back(partialAngles1[j]); + } + + UINT angles1Size = angles1.size(); + vector rationAngles(angles1Size), ratioLength(angles1Size + 1); + vector ratioX, ratioY; + + for (j = 0; j < angles1Size; j++) { + rationAngles[j] = fabs(angles1[j].second - angles2[j].second) / (angles1[j].second + angles2[j].second); + } + + UINT firstQuad1 = 0; + UINT firstQuad2 = 0; + UINT nextQuad1, nextQuad2; + + TRectD bbox1 = stroke1->getBBox(); + TRectD bbox2 = stroke2->getBBox(); + + double app, div; + double invTotalLen1 = stroke1->getLength(); + assert(invTotalLen1 > 0); + invTotalLen1 = 1.0 / invTotalLen1; + + double invTotalLen2 = stroke2->getLength(); + assert(invTotalLen2 > 0); + invTotalLen2 = 1.0 / invTotalLen2; + + for (j = 0; j <= angles1Size; j++) { + + if (j < angles1Size) { + nextQuad1 = angles1[j].first; + nextQuad2 = angles2[j].first; + } else { + nextQuad1 = stroke1->getChunkCount(); + nextQuad2 = stroke2->getChunkCount(); + } + + ratioLength[j] = fabs(stroke1->getLengthAtControlPoint(nextQuad1 * 2) * invTotalLen1 - stroke2->getLengthAtControlPoint(nextQuad2 * 2) * invTotalLen2); + + TPointD p1(stroke1->getChunk(nextQuad1 - 1)->getP2() - + stroke1->getChunk(firstQuad1)->getP0()); + p1.x = fabs(p1.x); + p1.y = fabs(p1.y); + + TPointD p2(stroke2->getChunk(nextQuad2 - 1)->getP2() - + stroke2->getChunk(firstQuad2)->getP0()); + p2.x = fabs(p2.x); + p2.y = fabs(p2.y); + + app = fabs(bbox1.getLx() * p2.x - bbox2.getLx() * p1.x); + div = (bbox1.getLx() * p2.x + bbox2.getLx() * p1.x); + if (div) + ratioX.push_back(app / div); + + app = fabs(bbox1.getLy() * p2.y - bbox2.getLy() * p1.y); + div = (bbox1.getLy() * p2.y + bbox2.getLy() * p1.y); + if (div) + ratioY.push_back(app / div); + + firstQuad1 = nextQuad1; + firstQuad2 = nextQuad2; + } + + double varAng, varX, varY, varLen; + + varX = average(ratioX); + varY = average(ratioY); + varLen = average(ratioLength); + varAng = average(rationAngles); + + double estimate = varX + varY + varAng + varLen; + /* + #ifdef _DEBUG + for(UINT dI=0; dI> &angles1, + const std::vector> &angles2, + double &bestValue, + std::vector &bestVector) +{ + assert(angles1.size() > angles2.size()); + + list> partialSolution; + + findBestSolution(stroke1, stroke2, + &(angles1[0]), angles1.size(), + angles2, + angles2.size(), partialSolution, + bestValue, bestVector); +} + +//-------------------------------------------------------------------------------------- + +void trivialSolution(const TStroke *stroke1, const TStroke *stroke2, + const std::vector> &angles1, + const std::vector> &angles2, + std::vector &solution) +{ + assert(angles1.size() > angles2.size()); + + UINT j; + double subStrokeRatio2; + + double invTotalLen2 = stroke2->getLength(); + assert(invTotalLen2); + invTotalLen2 = 1.0 / invTotalLen2; + + double invTotalLen1 = stroke1->getLength(); + assert(invTotalLen1 > 0); + invTotalLen1 = 1.0 / invTotalLen1; + + if (solution.size() != angles2.size()) { + assert(!"bad size for solution"); + solution.resize(angles2.size()); + } + + int toBeErased = angles1.size() - angles2.size(); + UINT count = 0; + + double diff, ratio, oldRatio = 100; + + subStrokeRatio2 = stroke2->getLengthAtControlPoint(angles2[count].first * 2) * invTotalLen2; + + for (j = 0; j < angles1.size() && count < solution.size(); j++) { + if (toBeErased == 0) { + solution[count++] = angles1[j].first; + } else { + ratio = stroke1->getLengthAtControlPoint(angles1[j].first * 2) * invTotalLen1; + assert(ratio > 0 && ratio <= 1); + + diff = ratio - subStrokeRatio2; + if (diff >= 0) { + if (fabs(diff) < fabs(oldRatio - subStrokeRatio2)) { + solution[count] = angles1[j].first; + oldRatio = 100; + } else { + assert(j > 0); + solution[count] = angles1[j - 1].first; + } + count++; + if (angles2.size() < count) + subStrokeRatio2 = stroke2->getLengthAtControlPoint(angles2[count].first * 2) * invTotalLen2; + else + subStrokeRatio2 = 1; + } else { + toBeErased--; + oldRatio = ratio; + } + } + } + assert(count == solution.size()); +} + +//-------------------------------------------------------------------------------------- + +TStroke *extract(const TStroke *source, UINT firstQuad, UINT lastQuad) +{ + UINT quadCount = source->getChunkCount(); + if (firstQuad >= quadCount) { + assert(!"bad quadric index"); + firstQuad = quadCount - 1; + } + if (lastQuad < firstQuad) { + assert(!"bad quadric index"); + lastQuad = firstQuad; + } + if (lastQuad >= quadCount) { + assert(!"bad quadric index"); + lastQuad = quadCount - 1; + } + + UINT cpIndex0 = firstQuad * 2; + UINT cpIndex1 = lastQuad * 2 + 2; + + vector points(cpIndex1 - cpIndex0 + 1); + UINT count = 0; + for (UINT j = cpIndex0; j <= cpIndex1; j++) { + points[count++] = source->getControlPoint(j); + } + + return new TStroke(points); +} + +//-------------------------------------------------------------------------------------- + +void sample(const TStroke *stroke, int samplingSize, vector &sampledPoint) +{ + double samplingFrequency = 1.0 / (double)samplingSize; + sampledPoint.resize(samplingSize); + + double totalLen = stroke->getLength(); + double step = totalLen * samplingFrequency; + double len = 0; + + for (int p = 0; p < samplingSize - 1; p++) { + sampledPoint[p] = stroke->getPointAtLength(len); + len += step; + } + sampledPoint.back() = stroke->getControlPoint(stroke->getControlPointCount() - 1); +} + +//-------------------------------------------------------------------------------------- + +class TInbetween::Imp +{ + +public: + //---------------------- + + struct StrokeTransform { + typedef enum { + EQUAL, + POINT, + GENERAL + } TransformationType; + + TPointD m_translate; + TPointD m_rotationAndScaleCenter; + double m_scaleX, m_scaleY, m_rotation; + + TransformationType m_type; + + //saved for optimization + TAffine m_inverse; + + vector m_firstStrokeCornerIndexes; + vector m_secondStrokeCornerIndexes; + }; + + //---------------------- + + TVectorImageP m_firstImage, m_lastImage; + + std::vector m_transformation; + + void computeTransformation(); + + void transferColor(const TVectorImageP &destination) const; + + TVectorImageP tween(double t) const; + + Imp(const TVectorImageP firstImage, const TVectorImageP lastImage) + : m_firstImage(firstImage), m_lastImage(lastImage) + { + computeTransformation(); + } +}; +//------------------------------------------------------------------- + +TInbetween::TInbetween(const TVectorImageP firstImage, const TVectorImageP lastImage) + : m_imp(new TInbetween::Imp(firstImage, lastImage)) +{ +} + +//------------------------------------------------------------------- + +TInbetween::~TInbetween() +{ + delete m_imp; +} + +//------------------------------------------------------------------- + +void TInbetween::Imp::computeTransformation() +{ + const UINT samplingPointNumber = 10; + const UINT bboxSamplingWeight = samplingPointNumber / 2; + + StrokeTransform transform; + double cs, sn, totalLen1, totalLen2, constK, constQ, constB, constD, constA; + UINT cpCount1, cpCount2; + TPointD stroke1Centroid, stroke2Centroid, stroke1Begin, stroke2Begin, stroke1End, stroke2End, versor1, versor2; + vector samplingPoint1(samplingPointNumber), samplingPoint2(samplingPointNumber); + TStroke *stroke1, *stroke2; + vector ratioSampling, weigths, subStrokeXScaling, subStrokeYScaling; + + UINT strokeCount1 = m_firstImage->getStrokeCount(); + UINT strokeCount2 = m_lastImage->getStrokeCount(); + if (strokeCount1 > strokeCount2) + strokeCount1 = strokeCount2; + + m_transformation.clear(); + m_transformation.reserve(strokeCount1); + + const int maxSubSetNum = (strokeCount1) ? 1000 / strokeCount1 : 1; + + UINT j, p; + + for (UINT i = 0; i < strokeCount1; i++) { + stroke1 = m_firstImage->getStroke(i); + stroke2 = m_lastImage->getStroke(i); + + //check if the strokes are equal + cpCount1 = stroke1->getControlPointCount(); + cpCount2 = stroke2->getControlPointCount(); + + transform.m_firstStrokeCornerIndexes.clear(); + transform.m_secondStrokeCornerIndexes.clear(); + transform.m_translate = TPointD(); + transform.m_rotationAndScaleCenter = TPointD(); + transform.m_scaleX = 0; + transform.m_scaleY = 0; + transform.m_rotation = 0; + + bool isEqual = true; + + if (cpCount1 == cpCount2) { + for (j = 0; j < cpCount1 && isEqual; j++) { + isEqual = (stroke1->getControlPoint(j) == stroke2->getControlPoint(j)); + } + } else + isEqual = false; + + if (isEqual) { + transform.m_type = StrokeTransform::EQUAL; + } else { + totalLen1 = stroke1->getLength(); + totalLen2 = stroke2->getLength(); + + if (totalLen1 == 0 || totalLen2 == 0) { + if (totalLen1 == 0 && totalLen2 == 0) { + transform.m_type = StrokeTransform::POINT; + } else { + transform.m_inverse = TAffine(); + transform.m_firstStrokeCornerIndexes.resize(2); + transform.m_firstStrokeCornerIndexes[0] = 0; + transform.m_firstStrokeCornerIndexes[1] = stroke1->getChunkCount(); + transform.m_secondStrokeCornerIndexes.resize(2); + transform.m_secondStrokeCornerIndexes[0] = 0; + transform.m_secondStrokeCornerIndexes[1] = stroke2->getChunkCount(); + } + } else { + + const double startMinAngle = 30.0; + vector> angles1, angles2; + + transform.m_type = StrokeTransform::GENERAL; + + double minAngle, maxAngle; + int minAngle1, maxAngle1, minAngle2, maxAngle2; + + angles1.clear(); + angles2.clear(); + + // TDebugMessage::getStream()<::max)(); + + if (angles1.size() != angles2.size()) { + bestValue = (std::numeric_limits::max)(); + + //-------------------------------------------------------------------------- + + if (isTooComplex(angles1.size(), angles2.size(), maxSubSetNum)) { + //debugStream <<"is too complex" << endl; + int firstAngle = (int)((angles1.size() < angles2.size()) ? minAngle2 : minAngle1); + int lastAngle = (int)((angles1.size() < angles2.size()) ? maxAngle1 : maxAngle2); + int bestAngle = (int)startMinAngle; + + if ((int)(angles1.size() + angles2.size()) < lastAngle - firstAngle + 1) { + double tempAngle; + vector sortedAngles1, sortedAngles2; + sortedAngles1.reserve(angles1.size()); + sortedAngles2.reserve(angles2.size()); + for (j = 0; j < angles1.size(); j++) { + tempAngle = angles1[j].second; + if (tempAngle >= firstAngle && tempAngle <= lastAngle) + sortedAngles1.push_back(tempAngle); + } + for (j = 0; j < angles2.size(); j++) { + tempAngle = angles2[j].second; + if (tempAngle >= firstAngle && tempAngle <= lastAngle) + sortedAngles2.push_back(tempAngle); + } + vector sortedAngles(sortedAngles1.size() + sortedAngles2.size()); + + std::sort(sortedAngles1.begin(), sortedAngles1.end()); + std::sort(sortedAngles2.begin(), sortedAngles2.end()); + std::merge(sortedAngles1.begin(), sortedAngles1.end(), sortedAngles2.begin(), sortedAngles2.end(), sortedAngles.begin()); + + for (j = 0; j < sortedAngles.size(); j++) { + int numAng1 = angleNumber(angles1, sortedAngles[j]); + int numAng2 = angleNumber(angles2, sortedAngles[j]); + double val = (numAng1 == numAng2) ? 0 : fabs((float)(numAng1 - numAng2)) / (numAng1 + numAng2); + if (val < bestValue) { + bestValue = val; + bestAngle = (int)(sortedAngles[j]); + if (bestValue == 0 || !isTooComplex(numAng1, numAng2, maxSubSetNum)) + break; + } + } + + } else //----------------------------------------------------- + { + for (int angle = firstAngle; angle <= lastAngle; angle++) { + int numAng1 = angleNumber(angles1, angle); + int numAng2 = angleNumber(angles2, angle); + double val = (numAng1 == numAng2) ? 0 : fabs((float)(numAng1 - numAng2)) / (numAng1 + numAng2); + if (val < bestValue) { + bestValue = val; + bestAngle = angle; + if (bestValue == 0 || !isTooComplex(numAng1, numAng2, maxSubSetNum)) + break; + } + } + } + + eraseSmallAngles(angles1, bestAngle); + eraseSmallAngles(angles2, bestAngle); + + /* + debugStream <<"bestAngle: "<< bestAngle << endl; + debugStream <<"num angoli 1: "<< angles1.size() << endl; + debugStream <<"num angoli 2: "<< angles2.size() << endl; + */ + } + //-------------------------------------------------------------------------- + + bestValue = (std::numeric_limits::max)(); + + if (angles1.size() == angles2.size()) { + transform.m_firstStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles1.size(); j++) + transform.m_firstStrokeCornerIndexes.push_back(angles1[j].first); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles2.size(); j++) + transform.m_secondStrokeCornerIndexes.push_back(angles2[j].first); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } else { + if (isTooComplex(angles1.size(), angles2.size(), maxSubSetNum)) { + if (angles1.size() > angles2.size()) { + transform.m_firstStrokeCornerIndexes.resize(angles2.size()); + trivialSolution(stroke1, stroke2, angles1, angles2, transform.m_firstStrokeCornerIndexes); + transform.m_firstStrokeCornerIndexes.insert(transform.m_firstStrokeCornerIndexes.begin(), 0); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles2.size(); j++) + transform.m_secondStrokeCornerIndexes.push_back(angles2[j].first); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } else { + transform.m_firstStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles1.size(); j++) + transform.m_firstStrokeCornerIndexes.push_back(angles1[j].first); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.resize(angles1.size()); + trivialSolution(stroke2, stroke1, angles2, angles1, transform.m_secondStrokeCornerIndexes); + transform.m_secondStrokeCornerIndexes.insert(transform.m_secondStrokeCornerIndexes.begin(), 0); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } + } else { + if (angles1.size() > angles2.size()) { + transform.m_firstStrokeCornerIndexes.resize(angles2.size()); + findBestSolution(stroke1, stroke2, angles1, angles2, bestValue, transform.m_firstStrokeCornerIndexes); + transform.m_firstStrokeCornerIndexes.insert(transform.m_firstStrokeCornerIndexes.begin(), 0); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles2.size(); j++) + transform.m_secondStrokeCornerIndexes.push_back(angles2[j].first); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } else { + transform.m_firstStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles1.size(); j++) + transform.m_firstStrokeCornerIndexes.push_back(angles1[j].first); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.resize(angles1.size()); + findBestSolution(stroke2, stroke1, angles2, angles1, bestValue, transform.m_secondStrokeCornerIndexes); + transform.m_secondStrokeCornerIndexes.insert(transform.m_secondStrokeCornerIndexes.begin(), 0); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } + } + } + } else { + transform.m_firstStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles1.size(); j++) + transform.m_firstStrokeCornerIndexes.push_back(angles1[j].first); + transform.m_firstStrokeCornerIndexes.push_back(stroke1->getChunkCount()); + + transform.m_secondStrokeCornerIndexes.push_back(0); + for (j = 0; j < angles2.size(); j++) + transform.m_secondStrokeCornerIndexes.push_back(angles2[j].first); + transform.m_secondStrokeCornerIndexes.push_back(stroke2->getChunkCount()); + } + + UINT cornerSize = transform.m_firstStrokeCornerIndexes.size(); + assert(cornerSize == transform.m_secondStrokeCornerIndexes.size()); + assert(cornerSize >= 2); + + double totalRadRotation = 0; + + TStroke *subStroke1 = 0; + TStroke *subStroke2 = 0; + + stroke1Centroid = stroke1->getCentroid(); + stroke2Centroid = stroke2->getCentroid(); + +#ifdef _DEBUG + assert(transform.m_firstStrokeCornerIndexes[0] == 0); + assert(transform.m_secondStrokeCornerIndexes[0] == 0); + assert(transform.m_firstStrokeCornerIndexes[cornerSize - 1] == stroke1->getChunkCount()); + assert(transform.m_secondStrokeCornerIndexes[cornerSize - 1] == stroke2->getChunkCount()); + for (j = 0; j < cornerSize - 1; j++) { + assert(transform.m_firstStrokeCornerIndexes[j] < transform.m_firstStrokeCornerIndexes[j + 1]); + assert(transform.m_secondStrokeCornerIndexes[j] < transform.m_secondStrokeCornerIndexes[j + 1]); + + assert(transform.m_firstStrokeCornerIndexes[j] < stroke1->getChunkCount()); + assert(transform.m_secondStrokeCornerIndexes[j] < stroke2->getChunkCount()); + } +#endif + + for (j = 0; j < cornerSize - 1; j++) { + ///////////////////////////////////////// sampling + + subStroke1 = extract(stroke1, transform.m_firstStrokeCornerIndexes[j], transform.m_firstStrokeCornerIndexes[j + 1] - 1); + sample(subStroke1, samplingPointNumber, samplingPoint1); + subStroke2 = extract(stroke2, transform.m_secondStrokeCornerIndexes[j], transform.m_secondStrokeCornerIndexes[j + 1] - 1); + sample(subStroke2, samplingPointNumber, samplingPoint2); + + ///////////////////////////////////////// compute Rotation + + ratioSampling.clear(); + ratioSampling.reserve(samplingPointNumber); + weigths.clear(); + weigths.reserve(samplingPointNumber); + + TPointD pOld, pNew; + // double totalW=0; + + for (p = 0; p < samplingPointNumber; p++) { + pOld = samplingPoint1[p]; + pNew = samplingPoint2[p]; + if (pOld == stroke1Centroid) + continue; + if (pNew == stroke2Centroid) + continue; + versor1 = normalize(pOld - stroke1Centroid); + versor2 = normalize(pNew - stroke2Centroid); + weigths.push_back(tdistance(pOld, stroke1Centroid) + tdistance(pNew, stroke2Centroid)); + cs = versor1 * versor2; + sn = cross(versor1, versor2); + double v = atan2(sn, cs); + ratioSampling.push_back(v); + } + delete subStroke1; + delete subStroke2; + subStroke1 = 0; + subStroke2 = 0; + + double radRotation = weightedAverage(ratioSampling, weigths); + + totalRadRotation += radRotation; + } + totalRadRotation /= (cornerSize - 1); + transform.m_rotation = TConsts::invOf_pi_180 * totalRadRotation; + + if (isAlmostZero(transform.m_rotation, 2)) { + transform.m_rotation = 0.0; + totalRadRotation = 0.0; + } + +#ifdef _DEBUG +// TDebugMessage::getStream()<<"rotation "<< transform.m_rotation; +// TDebugMessage::flush(); +#endif + + ///////////////////////////////////////// compute Scale + + if (transform.m_rotation == 0.0) { + + subStrokeXScaling.clear(); + subStrokeXScaling.reserve(cornerSize - 1); + subStrokeYScaling.clear(); + subStrokeYScaling.reserve(cornerSize - 1); + + for (j = 0; j < cornerSize - 1; j++) { + ///////////////////////////////////////// sampling + + subStroke1 = extract(stroke1, transform.m_firstStrokeCornerIndexes[j], transform.m_firstStrokeCornerIndexes[j + 1] - 1); + sample(subStroke1, samplingPointNumber, samplingPoint1); + subStroke2 = extract(stroke2, transform.m_secondStrokeCornerIndexes[j], transform.m_secondStrokeCornerIndexes[j + 1] - 1); + sample(subStroke2, samplingPointNumber, samplingPoint2); + + ///////////////////////////////////////// compute X Scale + + ratioSampling.clear(); + ratioSampling.reserve(samplingPointNumber + bboxSamplingWeight); + double appX, appY; + + TPointD appPoint; + double bboxXMin, bboxYMin, bboxXMax, bboxYMax; + bboxXMin = bboxYMin = (std::numeric_limits::max)(); + bboxXMax = bboxYMax = -(std::numeric_limits::max)(); + int h; + + for (h = 0; h < subStroke1->getControlPointCount(); ++h) { + appPoint = subStroke1->getControlPoint(h); + if (appPoint.x < bboxXMin) + bboxXMin = appPoint.x; + if (appPoint.x > bboxXMax) + bboxXMax = appPoint.x; + if (appPoint.y < bboxYMin) + bboxYMin = appPoint.y; + if (appPoint.y > bboxYMax) + bboxYMax = appPoint.y; + } + + appX = bboxXMax - bboxXMin; + appY = bboxYMax - bboxYMin; + + if (appX) { + bboxXMin = (std::numeric_limits::max)(); + bboxXMax = -(std::numeric_limits::max)(); + for (h = 0; h < subStroke2->getControlPointCount(); ++h) { + appPoint = subStroke2->getControlPoint(h); + if (appPoint.x < bboxXMin) + bboxXMin = appPoint.x; + if (appPoint.x > bboxXMax) + bboxXMax = appPoint.x; + } + appX = (isAlmostZero(appX, 1e-01)) ? -1 : (bboxXMax - bboxXMin) / appX; + for (UINT tms = 0; tms < bboxSamplingWeight && appX >= 0; tms++) + ratioSampling.push_back(appX); + + for (p = 0; p < samplingPointNumber; p++) { + appX = fabs(samplingPoint1[p].x - stroke1Centroid.x); + if (appX) + ratioSampling.push_back(fabs(samplingPoint2[p].x - stroke2Centroid.x) / appX); + } + + if (!ratioSampling.empty()) { + subStrokeXScaling.push_back(average(ratioSampling)); + } + } + + ///////////////////////////////////////// compute Y Scale + + ratioSampling.clear(); + ratioSampling.reserve(samplingPointNumber + bboxSamplingWeight); + + if (appY) { + bboxYMin = (std::numeric_limits::max)(); + bboxYMax = -(std::numeric_limits::max)(); + for (h = 0; h < subStroke2->getControlPointCount(); ++h) { + appPoint = subStroke2->getControlPoint(h); + if (appPoint.y < bboxYMin) + bboxYMin = appPoint.y; + if (appPoint.y > bboxYMax) + bboxYMax = appPoint.y; + } + + appY = (isAlmostZero(appY, 1e-01)) ? -1 : (bboxYMax - bboxYMin) / appY; + for (UINT tms = 0; tms < bboxSamplingWeight && appY >= 0; tms++) + ratioSampling.push_back(appY); + + for (p = 0; p < samplingPointNumber; p++) { + appY = fabs(samplingPoint1[p].y - stroke1Centroid.y); + if (appY) + ratioSampling.push_back(fabs(samplingPoint2[p].y - stroke2Centroid.y) / appY); + } + + if (!ratioSampling.empty()) { + subStrokeYScaling.push_back(average(ratioSampling)); + } + } + + delete subStroke1; + delete subStroke2; + subStroke1 = 0; + subStroke2 = 0; + } + + if (subStrokeXScaling.empty()) { + transform.m_scaleX = 1.0; + } else { + transform.m_scaleX = average(subStrokeXScaling); + if (isAlmostZero(transform.m_scaleX - 1.0)) + transform.m_scaleX = 1.0; + } + + if (subStrokeYScaling.empty()) { + transform.m_scaleY = 1.0; + } else { + transform.m_scaleY = average(subStrokeYScaling); + if (isAlmostZero(transform.m_scaleY - 1.0)) + transform.m_scaleY = 1.0; + } + /* + #ifdef _DEBUG + + TDebugMessage::getStream()<<"x scale "<< transform.m_scaleX ; + TDebugMessage::flush(); + TDebugMessage::getStream()<<"y scale "<< transform.m_scaleY ; + TDebugMessage::flush(); + #endif +*/ + } else { + subStrokeXScaling.clear(); + subStrokeXScaling.reserve(cornerSize - 1); + + for (j = 0; j < cornerSize - 1; j++) { + ///////////////////////////////////////// sampling + + subStroke1 = extract(stroke1, transform.m_firstStrokeCornerIndexes[j], transform.m_firstStrokeCornerIndexes[j + 1] - 1); + sample(subStroke1, samplingPointNumber, samplingPoint1); + subStroke2 = extract(stroke2, transform.m_secondStrokeCornerIndexes[j], transform.m_secondStrokeCornerIndexes[j + 1] - 1); + sample(subStroke2, samplingPointNumber, samplingPoint2); + + ///////////////////////////////////////// compute Scale + + ratioSampling.clear(); + ratioSampling.reserve(samplingPointNumber + bboxSamplingWeight); + + TRectD bbox1 = subStroke1->getBBox(); + double app = tdistance2(bbox1.getP00(), bbox1.getP11()); + if (app) { + TRectD bbox2 = TRotation(transform.m_rotation).inv() * subStroke2->getBBox(); + app = sqrt(tdistance2(bbox2.getP00(), bbox2.getP11()) / app); + + for (UINT tms = 0; tms < bboxSamplingWeight; tms++) + ratioSampling.push_back(app); + + double app; + for (p = 0; p < samplingPointNumber; p++) { + app = tdistance(samplingPoint1[p], stroke1Centroid); + if (app) { + ratioSampling.push_back(tdistance(samplingPoint2[p], stroke2Centroid) / app); + } + } + } + if (!ratioSampling.empty()) { + subStrokeXScaling.push_back(average(ratioSampling)); + } + + delete subStroke1; + delete subStroke2; + subStroke1 = 0; + subStroke2 = 0; + } + + if (subStrokeXScaling.empty()) { + transform.m_scaleX = transform.m_scaleY = 1.0; + } else { + transform.m_scaleX = transform.m_scaleY = average(subStrokeXScaling); + if (isAlmostZero(transform.m_scaleX - 1.0, 0.00001)) + transform.m_scaleX = transform.m_scaleY = 1.0; + } + /* + #ifdef _DEBUG + + TDebugMessage::getStream()<<"scale "<< transform.m_scaleX ; + TDebugMessage::flush(); + #endif +*/ + } + + ///////////////////////////////////////// compute centre of Rotation and Scaling + + vector vpOld(cornerSize), vpNew(cornerSize); + TPointD pOld, pNew; + + for (j = 0; j < cornerSize - 1; j++) { + vpOld[j] = stroke1->getChunk(transform.m_firstStrokeCornerIndexes[j])->getP0(); + vpNew[j] = stroke2->getChunk(transform.m_secondStrokeCornerIndexes[j])->getP0(); + } + vpOld[j] = stroke1->getControlPoint(stroke1->getControlPointCount()); + vpNew[j] = stroke2->getControlPoint(stroke2->getControlPointCount()); + + if (transform.m_rotation == 0.0) { + if (transform.m_scaleX == 1.0 && transform.m_scaleY == 1.0) { + transform.m_translate = stroke2Centroid - stroke1Centroid; + transform.m_rotationAndScaleCenter = TPointD(); + } else { + if (transform.m_scaleX == 1.0) { + transform.m_rotationAndScaleCenter.x = 0; + transform.m_translate.x = 0; + for (j = 0; j < cornerSize; j++) { + transform.m_translate.x += vpNew[j].x - vpOld[j].x; + } + transform.m_translate.x = transform.m_translate.x / cornerSize; + } else { + transform.m_rotationAndScaleCenter.x = 0; + + for (j = 0; j < cornerSize; j++) { + pOld = vpOld[j]; + pNew = vpNew[j]; + transform.m_rotationAndScaleCenter.x += (transform.m_scaleX * pOld.x - pNew.x) / (transform.m_scaleX - 1.0); + } + transform.m_rotationAndScaleCenter.x = transform.m_rotationAndScaleCenter.x / cornerSize; + transform.m_translate.x = 0; + } + + if (transform.m_scaleY == 1.0) { + transform.m_rotationAndScaleCenter.y = 0; + transform.m_translate.y = 0; + for (j = 0; j < cornerSize; j++) { + transform.m_translate.y += vpNew[j].y - vpOld[j].y; + } + transform.m_translate.y = transform.m_translate.y / cornerSize; + } else { + transform.m_rotationAndScaleCenter.y = 0; + + for (j = 0; j < cornerSize; j++) { + pOld = vpOld[j]; + pNew = vpNew[j]; + transform.m_rotationAndScaleCenter.y += (transform.m_scaleY * pOld.y - pNew.y) / (transform.m_scaleY - 1.0); + } + transform.m_rotationAndScaleCenter.y = transform.m_rotationAndScaleCenter.y / cornerSize; + transform.m_translate.y = 0; + } + } + + } else { + assert(transform.m_scaleX = transform.m_scaleY); + + cs = transform.m_scaleX * cos(totalRadRotation); + sn = transform.m_scaleX * sin(totalRadRotation); + + // scelgo punti da usare come vincolo, per ottenere la translazione, dato un centro di rotazione + + // dato il punto pOld e pNew si calcola analiticamnete il punto di rotazione e scala + // che minimizza la traslazione aggiuntiva e la traslazione stessa + + for (j = 0; j < cornerSize; j++) { + pOld = vpOld[j]; + pNew = vpNew[j]; + constK = pNew.x - cs * pOld.x + sn * pOld.y; + constQ = pNew.y - sn * pOld.x - cs * pOld.y; + constB = 2 * (constK * (cs - 1.) + sn * constQ); + constD = 2 * (constQ * (cs - 1.) - sn * constK); + constA = transform.m_scaleX * transform.m_scaleX + 1 - 2 * cs; + assert(constA > 0); + constA = 1.0 / (2 * constA); + transform.m_rotationAndScaleCenter.x += -constB * constA; + transform.m_rotationAndScaleCenter.y += -constD * constA; + } + + transform.m_rotationAndScaleCenter = transform.m_rotationAndScaleCenter * (1.0 / (double)cornerSize); + + transform.m_translate.x = (cs - 1.0) * transform.m_rotationAndScaleCenter.x - sn * transform.m_rotationAndScaleCenter.y + constK; + + transform.m_translate.y = sn * transform.m_rotationAndScaleCenter.x + (cs - 1.0) * transform.m_rotationAndScaleCenter.y + constQ; + } + + ///////////////////////////////////////// + + transform.m_inverse = (TTranslation(transform.m_translate) * + TScale(transform.m_rotationAndScaleCenter, transform.m_scaleX, transform.m_scaleY) * + TRotation(transform.m_rotationAndScaleCenter, transform.m_rotation)) + .inv(); + + //debugStream.close(); + } // end if !isPoint + + } // end if !isEqual + + m_transformation.push_back(transform); + + } // end for each stroke +} + +//------------------------------------------------------------------- + +TVectorImageP TInbetween::Imp::tween(double t) const +{ + const double step = 5.0; + const double interpolateError = 1.0; + + TVectorImageP vi = new TVectorImage; + + UINT strokeCount1 = m_firstImage->getStrokeCount(); + UINT strokeCount2 = m_lastImage->getStrokeCount(); + vi->setPalette(m_firstImage->getPalette()); + if (strokeCount1 > strokeCount2) + strokeCount1 = strokeCount2; + + assert(m_transformation.size() == strokeCount1); + + double totalLen1, totalLen2, len1, len2, step1, step2; + vector points; + TStroke *stroke1, *stroke2, *subStroke1, *subStroke2, *stroke; + + TAffine mt, invMatrix; + TThickPoint point2, finalPoint; + UINT i, j, cp, cpSize; + + for (i = 0; i < strokeCount1; i++) { + + stroke1 = m_firstImage->getStroke(i); + stroke2 = m_lastImage->getStroke(i); + + if (m_transformation[i].m_type == StrokeTransform::EQUAL) { + stroke = new TStroke(*stroke1); + } else { + points.clear(); + totalLen1 = stroke1->getLength(); + totalLen2 = stroke2->getLength(); + ; + + if (stroke1->getControlPointCount() == stroke2->getControlPointCount() && + stroke1->isSelfLoop() && stroke2->isSelfLoop()) { + for (int i = 0; i < stroke1->getControlPointCount(); i++) { + TThickPoint p0 = stroke1->getControlPoint(i); + TThickPoint p1 = stroke2->getControlPoint(i); + points.push_back(p0 * (1 - t) + p1 * t); + } + stroke = new TStroke(points); + } else if (m_transformation[i].m_type == StrokeTransform::POINT) { + TThickPoint pOld = stroke1->getThickPointAtLength(0.5 * totalLen1); + TThickPoint pNew = stroke2->getThickPointAtLength(0.5 * totalLen2); + points.push_back(pOld * (1 - t) + pNew * t); + points.push_back(points[0]); + points.push_back(points[0]); + stroke = new TStroke(points); + } else { + mt = (m_transformation[i].m_inverse.isIdentity()) + ? TAffine() + : TTranslation(m_transformation[i].m_translate * t) * + TScale(m_transformation[i].m_rotationAndScaleCenter, (1 - t) + t * m_transformation[i].m_scaleX, (1 - t) + t * m_transformation[i].m_scaleY) * + TRotation(m_transformation[i].m_rotationAndScaleCenter, m_transformation[i].m_rotation * t); + + UINT cornerSize = m_transformation[i].m_firstStrokeCornerIndexes.size(); + assert(cornerSize == m_transformation[i].m_secondStrokeCornerIndexes.size()); + if (cornerSize > m_transformation[i].m_secondStrokeCornerIndexes.size()) + cornerSize = m_transformation[i].m_secondStrokeCornerIndexes.size(); + + assert(cornerSize >= 2); + + std::vector controlPoints; + + //if not m_transformation[i].m_findCorners => detect corner return different size =>cornerSize==2 + // assert(!m_transformation[i].m_findCorners || cornerSize==2); + + for (j = 0; j < cornerSize - 1; j++) { + points.clear(); + subStroke1 = extract(stroke1, m_transformation[i].m_firstStrokeCornerIndexes[j], m_transformation[i].m_firstStrokeCornerIndexes[j + 1] - 1); + subStroke2 = extract(stroke2, m_transformation[i].m_secondStrokeCornerIndexes[j], m_transformation[i].m_secondStrokeCornerIndexes[j + 1] - 1); + + totalLen1 = subStroke1->getLength(); + totalLen2 = subStroke2->getLength(); + + if (totalLen1 > totalLen2) { + step1 = step; + step2 = (totalLen2 / totalLen1) * step; + } else { + step1 = (totalLen1 / totalLen2) * step; + step2 = step; + } + + len1 = 0; + len2 = 0; + + while (len1 <= totalLen1 && len2 <= totalLen2) { + point2 = subStroke2->getThickPointAtLength(len2); + point2 = TThickPoint(m_transformation[i].m_inverse * subStroke2->getThickPointAtLength(len2), point2.thick); + finalPoint = subStroke1->getThickPointAtLength(len1) * (1 - t) + t * point2; + + points.push_back(TThickPoint(mt * (finalPoint), finalPoint.thick)); + len1 += step1; + len2 += step2; + } + point2 = subStroke2->getThickPointAtLength(totalLen2); + point2 = TThickPoint(m_transformation[i].m_inverse * subStroke2->getThickPointAtLength(totalLen2), point2.thick); + finalPoint = subStroke1->getThickPointAtLength(totalLen1) * (1 - t) + t * point2; + + points.push_back(TThickPoint(mt * (finalPoint), finalPoint.thick)); + + stroke = TStroke::interpolate(points, interpolateError, false + /*m_transformation[i].m_findCorners*/); + + if (j == 0) + controlPoints.push_back(stroke->getControlPoint(0)); + else + controlPoints.back() = (controlPoints.back() + stroke->getControlPoint(0)) * 0.5; + + cpSize = stroke->getControlPointCount(); + for (cp = 1; cp < cpSize; cp++) { + controlPoints.push_back(stroke->getControlPoint(cp)); + } + + delete subStroke1; + delete subStroke2; + delete stroke; + subStroke1 = 0; + subStroke2 = 0; + stroke = 0; + } + + stroke = new TStroke(controlPoints); + } + } + + if (stroke1->isSelfLoop() && stroke2->isSelfLoop()) + stroke->setSelfLoop(); + + stroke->setStyle(stroke1->getStyle()); + stroke->outlineOptions() = stroke1->outlineOptions(); + VIStroke *vs = new VIStroke(stroke, m_firstImage->getVIStroke(i)->m_groupId); + vi->m_imp->m_strokes.push_back(vs); + + } //end for each stroke + + if (m_firstImage->isComputedRegionAlmostOnce()) + transferColor(vi); + + return vi; +} + +//------------------------------------------------------------------- + +void TInbetween::Imp::transferColor(const TVectorImageP &destination) const +{ + const TVectorImageP &original = m_firstImage; + destination->setPalette(original->getPalette()); + + destination->findRegions(); + + if (destination->getRegionCount()) { + UINT strokeCount1 = original->getStrokeCount(); + UINT strokeCount2 = destination->getStrokeCount(); + if (strokeCount1 > strokeCount2) + strokeCount1 = strokeCount2; + + for (UINT i = 0; i < strokeCount1; i++) { + TVectorImage::transferStrokeColors(original, i, destination, i); + } + } +} + +//------------------------------------------------------------------- + +TVectorImageP TInbetween::tween(double t) const +{ + return m_imp->tween(t); +} + +//------------------------------------------------------------------- diff --git a/toonz/sources/common/tvrender/tofflinegl.cpp b/toonz/sources/common/tvrender/tofflinegl.cpp new file mode 100644 index 0000000..5ea9202 --- /dev/null +++ b/toonz/sources/common/tvrender/tofflinegl.cpp @@ -0,0 +1,949 @@ + + +// TnzCore includes +#include "texception.h" +#include "tvectorgl.h" +#include "tvectorimage.h" +#include "trasterimage.h" +#include "tgl.h" +#include "tthreadmessage.h" +#include "tsystem.h" +#include "trop.h" + +// Platform-specific includes +#if defined(LINUX) + +#include +#include + +#include "xscopedlock.h" +#include "tthread.h" + +#elif MACOSX + +#include "qtofflinegl.h" + +#endif + +#include "tofflinegl.h" + +#ifndef checkErrorsByGL +#define checkErrorsByGL \ + { \ + GLenum err = glGetError(); \ + assert(err != GL_INVALID_ENUM); \ + assert(err != GL_INVALID_VALUE); \ + assert(err != GL_INVALID_OPERATION); \ + assert(err != GL_STACK_OVERFLOW); \ + assert(err != GL_STACK_UNDERFLOW); \ + assert(err != GL_OUT_OF_MEMORY); \ + assert(err == GL_NO_ERROR); \ + } +#endif + +#undef checkErrorsByGL +#define checkErrorsByGL /**/ + +using namespace std; + +TGLContextManager *currentContextManager = 0; + +void TOfflineGL::setContextManager(TGLContextManager *contextManager) +{ + currentContextManager = contextManager; + if (contextManager) + contextManager->store(); +} + +//============================================================================= +// WIN32Implementation : implementazione offlineGL WIN32 +//----------------------------------------------------------------------------- + +#ifdef WIN32 + +namespace +{ +//We found out that our implementation of win32 opengl contexts can be someway +//not thread-safe. Deadlocks and errors could happen for wgl* and GDI functions +//on particular configurations (notably, Windows 7). So we mutex them as +//a temporary workaround. +static QMutex win32ImpMutex; +} + +//------------------------------- + +class WIN32Implementation : public TOfflineGL::Imp +{ +public: + HDC m_offDC; + HGDIOBJ m_oldobj; + HGLRC m_hglRC; + HBITMAP m_offDIB; + void *m_offData; + + //----------------------------------------------------------------------------- + + WIN32Implementation(TDimension rasterSize, const TOfflineGL::Imp *shared = 0) + : TOfflineGL::Imp(rasterSize.lx, rasterSize.ly) + { + m_offData = 0; + createContext(rasterSize, shared); //makeCurrent is called at the end of this + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + doneCurrent(); //doneCurrent must therefore be called here + } + + //----------------------------------------------------------------------------- + + ~WIN32Implementation() + { + QMutexLocker locker(&win32ImpMutex); + + BOOL ret = wglMakeCurrent(m_offDC, NULL); + assert(ret == TRUE); + wglDeleteContext(m_hglRC); + SelectObject(m_offDC, m_oldobj); + DeleteObject(m_offDC); + + // si potrebbe passare un parametro che evita di distruggere la bitmap. + // In tal caso il raster dovrebbe diventare owner del buffer, ma attualmente + // questo non e' settabile in TRaster: quando gli si passa da fuori un buffer, + // automaticamente bufferOwner viene settato a false e non e' modificabile! + DeleteObject(m_offDIB); + } + + //----------------------------------------------------------------------------- + + void makeCurrent() + { + QMutexLocker locker(&win32ImpMutex); + + int ret = wglMakeCurrent(m_offDC, m_hglRC); + assert(ret == TRUE); + } + + //----------------------------------------------------------------------------- + + void doneCurrent() + { + QMutexLocker locker(&win32ImpMutex); + + glFlush(); + glFinish(); + assert(glGetError() == 0); + wglMakeCurrent(NULL, NULL); + } + + //----------------------------------------------------------------------------- + + void initBITMAPINFO(BITMAPINFO &info, const TDimension rasterSize) + { + memset(&info, 0, sizeof(BITMAPINFOHEADER)); + + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biWidth = rasterSize.lx; + info.bmiHeader.biHeight = rasterSize.ly; + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 32; + info.bmiHeader.biCompression = BI_RGB; + info.bmiHeader.biSizeImage = 0; + info.bmiHeader.biXPelsPerMeter = 1000; + info.bmiHeader.biYPelsPerMeter = 1000; + info.bmiHeader.biClrUsed = 0; + info.bmiHeader.biClrImportant = 0; + } + + //----------------------------------------------------------------------------- + + void createContext(TDimension rasterSize, const TOfflineGL::Imp *shared) + { + QMutexLocker locker(&win32ImpMutex); + + BITMAPINFO info; + + initBITMAPINFO(info, rasterSize); + + // open an offscreen device + m_offDC = CreateCompatibleDC(NULL); + + // and a bitmap image + m_offDIB = CreateDIBSection(m_offDC, &info, DIB_RGB_COLORS, &m_offData, NULL, 0); + + assert(m_offDIB); + assert(m_offData); + + if (!m_offDIB || !m_offData) + throw TException("cannot create OpenGL context. Check system resources!"); + + int dataSize = rasterSize.lx * rasterSize.ly * 4; // number of byte of raster + + memset(m_offData, 0, dataSize); + + m_oldobj = SelectObject(m_offDC, m_offDIB); // select BIB to write + + static PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + 0 | (false ? (PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER) : (PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI)) | PFD_SUPPORT_OPENGL, // support OpenGL + PFD_TYPE_RGBA, // RGBA type + 32, // 32-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 8, // no alpha buffer /*===*/ + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 32, // max stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + + // get the best available match of pixel format for the device context + int iPixelFormat = ChoosePixelFormat(m_offDC, &pfd); + assert(iPixelFormat != 0); + + // make that the pixel format of the device context + int ret = SetPixelFormat(m_offDC, iPixelFormat, &pfd); + assert(ret == TRUE); + + // make a valid context for OpenGL rendering + + m_hglRC = wglCreateContext(m_offDC); + assert(m_hglRC); + + if (!m_hglRC) + throw TException("cannot create OpenGL context. Check system resources!"); + + if (shared) { + // Share shared's display lists + const WIN32Implementation *sharedImp = dynamic_cast(shared); + assert(sharedImp); + + bool ok = wglShareLists(sharedImp->m_hglRC, m_hglRC); + assert(ok); + } + + ret = wglMakeCurrent(m_offDC, m_hglRC); + assert(ret == TRUE); + } + + //----------------------------------------------------------------------------- + + void swapRedBlueChannels(void *buffer, int bufferSize) // Flips The Red And Blue Bytes (WidthxHeight) + { + void *b = buffer; // Pointer To The Buffer + +#ifdef x64 + int size = bufferSize; + UCHAR *pix = (UCHAR *)b; + while (size > 0) { + UCHAR r = *pix; + UCHAR b = *(pix + 2); + *pix = b; + *(pix + 2) = r; + pix += 4; + size--; + } +/*unsigned long ebx = (unsigned long)b; + while(size>0) + { + unsigned char al =__readgsbyte(ebx); + unsigned char ah =__readgsbyte(ebx+2); + __writegsbyte(ebx+2,al); + __writegsbyte(ebx,ah); + ebx+=4; + size--; + }*/ +#else + __asm // Assembler Code To Follow + { + mov ecx, bufferSize // Counter Set To Dimensions Of Our Memory Block + mov ebx, b // Points ebx To Our Data (b) + label: // Label Used For Looping + mov al,[ebx+0] // Loads Value At ebx Into al + mov ah,[ebx+2] // Loads Value At ebx+2 Into ah + mov [ebx+2],al // Stores Value In al At ebx+2 + mov [ebx+0],ah // Stores Value In ah At ebx + + add ebx,4 // Moves Through The Data By 4 Bytes + dec ecx // Decreases Our Loop Counter + jnz label // If Not Zero Jump Back To Label + } +#endif + } + + //----------------------------------------------------------------------------- + + void getRaster(TRaster32P raster) + { + makeCurrent(); + glFlush(); + + int lx = raster->getLx(); + int ly = raster->getLy(); + + raster->lock(); + glReadPixels(0, 0, lx, ly, + GL_RGBA /*GL_BGRA_EXT*/, GL_UNSIGNED_BYTE, + raster->getRawData()); + + swapRedBlueChannels(raster->getRawData(), lx * ly); + raster->unlock(); + } +}; + +// default imp generator +TOfflineGL::Imp *defaultOfflineGLGenerator(const TDimension &dim, const TOfflineGL::Imp *shared) +{ + return new WIN32Implementation(dim, shared); +} + +//============================================================================= +// XImplementation : implementazione offlineGL Server X (MACOSX & LINUX) +//----------------------------------------------------------------------------- + +#elif defined(LINUX) + +class XImplementation : public TOfflineGL::Imp +{ +public: + Display *m_dpy; + GLXContext m_context; + GLXPixmap m_pixmap; + Pixmap m_xpixmap; + + //----------------------------------------------------------------------------- + + XImplementation(TDimension rasterSize) + { + createContext(rasterSize); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + //----------------------------------------------------------------------------- + + ~XImplementation() + { + glXDestroyContext(m_dpy, m_context); + m_context = 0; + safeGlXMakeCurrent(true); + XCloseDisplay(m_dpy); + } + + //----------------------------------------------------------------------------- + + bool safeGlXMakeCurrent(bool isDtor = false) + { + static std::map m_glxContext; + static TThread::Mutex mutex; + + QMutexLocker sl(&mutex); + pthread_t self = pthread_self(); + std::map::iterator it = m_glxContext.find(self); + if (((it != m_glxContext.end()) && (it->second != m_context)) || (it == m_glxContext.end())) { + // cout << "calling GLXMakeCurrent " << self << " " << m_context << endl; + Bool ret; + if (!isDtor) + ret = glXMakeCurrent(m_dpy, m_pixmap, m_context); + m_glxContext[self] = m_context; + return ret; + } + // cout << "don't call GLXMakeCurrent " << self << " " << m_context << endl; + return true; + } + + //----------------------------------------------------------------------------- + + void makeCurrent() + { + XScopedLock xsl; + + //Bool ret = glXMakeCurrent(m_dpy,m_pixmap,m_context); + + Bool ret = safeGlXMakeCurrent(); + assert(ret == True); + } + + //----------------------------------------------------------------------------- + + // DA IMPLEMENTARE !!! + + void doneCurrent() + { + } + + //----------------------------------------------------------------------------- + + void createContext(TDimension rasterSize) + { + m_dpy = XOpenDisplay(NULL); + Window win = DefaultRootWindow(m_dpy); + int attribList[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + // GLX_ALPHA_SIZE, 1, + GLX_STENCIL_SIZE, 8, + + // GLX_DEPTH_SIZE, 24, + + None}; + + int dbAttrib[] = {GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, + None}; + + int w = rasterSize.lx; + int h = rasterSize.ly; + + XVisualInfo *vis = glXChooseVisual(m_dpy, DefaultScreen(m_dpy), attribList); + if (!vis) { + std::cout << "unable to create sb visual" << std::endl; + vis = glXChooseVisual(m_dpy, DefaultScreen(m_dpy), dbAttrib); + assert(vis && "unable to create db visual"); + } + + m_context = glXCreateContext(m_dpy, vis, 0, False); + // std::cout << "Direct rendering: " << (glXIsDirect(m_dpy, m_context) ? "Yes" : "No" )<< std::endl; + + if (!m_context) + assert("not m_context" && false); + TRaster32P raster(w, h); + + m_xpixmap = XCreatePixmap(m_dpy, win, w, h, vis->depth); + + assert(m_xpixmap && "not m_xpixmap"); + + m_pixmap = glXCreateGLXPixmap(m_dpy, vis, m_xpixmap); + if (!m_pixmap) + assert("not m_pixmap" && m_pixmap); + /* + Bool ret = glXMakeCurrent(m_dpy, + m_pixmap, + m_context); +*/ + Bool ret = safeGlXMakeCurrent(); + assert(ret); + + m_raster = raster; + } + +//----------------------------------------------------------------------------- + +#if defined(MACOSX) +#if defined(powerpc) + void rightRotateBits(UCHAR *buf, int bufferSize) + { + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = app >> 8 | app << 24; + } + } +#else + void rightRotateBits(UCHAR *buf, int bufferSize) + { + UINT *buffer = (UINT *)buf; + register UINT app; + for (int i = 0; i < bufferSize; i++, buffer++) { + app = *buffer; + *buffer = (app >> 16 & 0x000000ff) | (app << 16 & 0x00ff0000) | (app & 0xff00ff00); + } + } +#endif +#endif + //----------------------------------------------------------------------------- + + const TRaster32P &getRaster() + { + makeCurrent(); + glFlush(); + + int lx = m_raster->getLx(); + int ly = m_raster->getLy(); + m_raster->lock(); + glReadPixels(0, 0, lx, ly, + GL_RGBA /*GL_BGRA_EXT*/, GL_UNSIGNED_BYTE, + m_raster->getRawData()); + +#if defined(MACOSX) + rightRotateBits(m_raster->getRawData(), lx * ly); +#warning "to do" +#endif + m_raster->unlock(); + return m_raster; + } + + //----------------------------------------------------------------------------- + + int getLx() const + { + return m_raster->getLx(); + } + + //----------------------------------------------------------------------------- + + int getLy() const + { + return m_raster->getLy(); + } +}; + +TOfflineGL::Imp *defaultOfflineGLGenerator(const TDimension &dim) +{ + return new XImplementation(dim); +} + +#elif MACOSX + +TOfflineGL::Imp *defaultOfflineGLGenerator(const TDimension &dim, const TOfflineGL::Imp *shared) +{ + return new QtOfflineGL(dim, shared); +} + +#endif + +//============================================================================= + +//----------------------------------------------------------------------------- + +// current imp generator +namespace +{ +TOfflineGL::ImpGenerator *currentImpGenerator = defaultOfflineGLGenerator; +} // namespace + +//============================================================================= +// TOfflineGL +//----------------------------------------------------------------------------- + +//namespace { + +class MessageCreateContext : public TThread::Message +{ + friend class TOfflineGL; + + TOfflineGL *m_ogl; + TDimension m_size; + const TOfflineGL::Imp *m_shared; + +public: + MessageCreateContext(TOfflineGL *ogl, const TDimension &size, const TOfflineGL::Imp *shared) + : m_ogl(ogl), m_size(size), m_shared(shared) {} + + void onDeliver() + { + m_ogl->m_imp = currentImpGenerator(m_size, m_shared); + } + + TThread::Message *clone() const + { + return new MessageCreateContext(*this); + } +}; + +//} // namespace + +//-------------------------------------------------- + +TOfflineGL::TOfflineGL(TDimension dim, const TOfflineGL *shared) + : m_imp(0) +{ +#if defined(LINUX) + XScopedLock xsl; +#endif + + const TOfflineGL::Imp *sharedImp = shared ? shared->m_imp : 0; + + /* + 元のコードは(別スレッドから呼び出すための) offline renderer を作って main thread に dispatch するという訳のわからないことをしていたが Q*GLContext は thread context を超えられないので直接生成してこのコンテキストで閉じる. + 別スレッドには dispatch しない. + */ + m_imp = currentImpGenerator(dim, sharedImp); + + initMatrix(); +} + +//----------------------------------------------------------------------------- + +TOfflineGL::TOfflineGL(const TRaster32P &raster, const TOfflineGL *shared) +{ +#if defined(LINUX) + XScopedLock xsl; +#endif + + //m_imp = new Imp(raster->getSize()); + + m_imp = currentImpGenerator(raster->getSize(), shared->m_imp); + + initMatrix(); + + glRasterPos2d(0, 0); + raster->lock(); + glDrawPixels(raster->getLx(), raster->getLy(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, raster->getRawData()); + raster->unlock(); +} + +//----------------------------------------------------------------------------- + +TOfflineGL::~TOfflineGL() +{ + delete m_imp; +} + +//----------------------------------------------------------------------------- + +TOfflineGL::ImpGenerator *TOfflineGL::defineImpGenerator(TOfflineGL::ImpGenerator *impGenerator) +{ + TOfflineGL::ImpGenerator *ret = currentImpGenerator; + currentImpGenerator = impGenerator; + return ret; +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::makeCurrent() +{ + if (currentContextManager) + currentContextManager->store(); + // Tutto il codice è stato spostato dentro Imp + m_imp->makeCurrent(); + assert(glGetError() == GL_NO_ERROR); +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::doneCurrent() +{ + m_imp->doneCurrent(); + if (currentContextManager) { + currentContextManager->restore(); + } +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::initMatrix() +{ + m_imp->makeCurrent(); + + // cfr. help: OpenGL/Programming tip/OpenGL Correctness Tips + glViewport(0, 0, m_imp->getLx(), m_imp->getLy()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, m_imp->getLx(), 0, m_imp->getLy()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + //glTranslatef(0.375, 0.375, 0.0); //WRONG + + /* (From Daniele) + +Quoting from the aforementioned source: + +"An optimum compromise that allows all primitives to be specified at integer +positions, while still ensuring predictable rasterization, is to translate x +and y by 0.375, as shown in the following code sample. Such a translation +keeps polygon and pixel image edges safely away from the centers of pixels, +while moving line vertices close enough to the pixel centers" + + NOTE: This is not an acceptable excuse in our case - as we're NOT USING + INTEGER COORDINATES ONLY. OpenGL has all the rights to render pixels + at integer coordinates across the neighbouring 4 pixels - and their + (0.5, 0.5) translations at the EXACT screen pixel. + */ +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::clear(TPixel32 color) +{ + const double maxValue = 255.0; + makeCurrent(); + glClearColor( + (double)color.r / maxValue, + (double)color.g / maxValue, + (double)color.b / maxValue, + (double)color.m / maxValue); + glClear(GL_COLOR_BUFFER_BIT); +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::draw(TVectorImageP image, const TVectorRenderData &rd, bool doInitMatrix) +{ + checkErrorsByGL; + makeCurrent(); + checkErrorsByGL; + + if (doInitMatrix) { + initMatrix(); + checkErrorsByGL; + } + + if (image) { + checkErrorsByGL; + tglDraw(rd, image.getPointer()); + checkErrorsByGL; + } + + checkErrorsByGL; + glFlush(); + checkErrorsByGL; +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::draw(TRasterImageP ri, const TAffine &aff, bool doInitMatrix) +{ + makeCurrent(); + + if (doInitMatrix) + initMatrix(); + + TRaster32P ras32 = ri->getRaster(); + if (!ras32) + return; + + int lx = ras32->getLx(); + int ly = ras32->getLy(); + // lx e ly devono essere potenze di due + assert((lx & (lx - 1)) == 0); + assert((ly & (ly - 1)) == 0); + + glPushMatrix(); + tglMultMatrix(aff); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexEnvf(GL_TEXTURE_ENV, + GL_TEXTURE_ENV_MODE, + GL_DECAL); + + glEnable(GL_TEXTURE_2D); + + /* + TNZ_MACHINE_CHANNEL_ORDER_BGRM + TNZ_MACHINE_CHANNEL_ORDER_MBGR + TNZ_MACHINE_CHANNEL_ORDER_RGBM + TNZ_MACHINE_CHANNEL_ORDER_MRGB + */ + + GLenum fmt = TGL_FMT; + /* + #ifdef TNZ_MACHINE_CHANNEL_ORDER_BGRM + GL_BGRA_EXT; + #elif TNZ_MACHINE_CHANNEL_ORDER_MBGR + GL_ABGR_EXT; + #elif TNZ_MACHINE_CHANNEL_ORDER_RGBM + GL_RGBA; + #elif TNZ_MACHINE_CHANNEL_ORDER_MRGB + #warning "to do" + GL_ABGR_EXT; + #else + Error PLATFORM NOT SUPPORTED + #endif +*/ + + // Generate a texture id and bind it. + GLuint texId; + glGenTextures(1, &texId); + + glBindTexture(GL_TEXTURE_2D, texId); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, + ras32->getWrap() != ras32->getLx() ? ras32->getWrap() : 0); + + ras32->lock(); + + glTexImage2D( + GL_TEXTURE_2D, // target (is a 2D texture) + 0, // is one level only + GL_RGB, // number of component of a pixel + lx, // size width + ly, // height + 0, // size of a border + fmt, + GL_UNSIGNED_BYTE, // + ras32->getRawData()); + + ras32->unlock(); + + double halfWidth = 0.5 * lx; + double halfHeight = 0.5 * ly; + + double dpix = 1, dpiy = 1; + ri->getDpi(dpix, dpiy); + + if (dpix != 0 && dpiy != 0) { + double unit = 100; + halfWidth *= unit / dpix; + halfHeight *= unit / dpiy; + } + + glBegin(GL_QUAD_STRIP); + glTexCoord2d(0, 0); + glVertex2d(-halfWidth, -halfHeight); + glTexCoord2d(1, 0); + glVertex2d(halfWidth, -halfHeight); + glTexCoord2d(0, 1); + glVertex2d(-halfWidth, halfHeight); + glTexCoord2d(1, 1); + glVertex2d(halfWidth, halfHeight); + glEnd(); + glDisable(GL_TEXTURE_2D); + + glPopMatrix(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + // Delete texture + glDeleteTextures(1, &texId); + + glFlush(); +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::flush() +{ + makeCurrent(); + glFlush(); +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::getRaster(TRaster32P raster) +{ + assert(raster->getLx() <= getLx() && raster->getLy() <= getLy()); + if (raster->getWrap() == raster->getLx()) { + m_imp->getRaster(raster); + } else { + //There are 2 possible solutions: use glReadPixels multiple times for each row of input raster, + //OR allocate a new contiguous buffer, use glReadPixels once, and then memcpy each row. + //It actually seems that the *latter* is actually the fastest solution, although it requires + //allocating a temporary buffer of the same size of the requested raster... + //I also could not actually manage to make the former work - the code seemed right but results were weird... + TRaster32P ras32(raster->getSize()); + m_imp->getRaster(ras32); + TRop::copy(raster, ras32); + } +} + +//----------------------------------------------------------------------------- + +void TOfflineGL::getRaster(TRasterP raster) +{ + assert(raster->getLx() <= getLx() && raster->getLy() <= getLy()); + TRaster32P ras32 = raster; + if (ras32 && (raster->getWrap() == raster->getLx())) { + m_imp->getRaster(ras32); + } else { + ras32 = TRaster32P(raster->getSize()); + m_imp->getRaster(ras32); + TRop::copy(raster, ras32); + } +} + +//----------------------------------------------------------------------------- + +TRaster32P TOfflineGL::getRaster() +{ + TRaster32P raster(getLx(), getLy()); + m_imp->getRaster(raster); + return raster; +} + +//----------------------------------------------------------------------------- + +int TOfflineGL::getLx() const +{ + return m_imp->getLx(); +} + +//----------------------------------------------------------------------------- + +int TOfflineGL::getLy() const +{ + return m_imp->getLy(); +} + +//----------------------------------------------------------------------------- + +//============================================================================= + +namespace +{ + +struct DimensionLess : public std::binary_function { + bool operator()(const TDimension &d1, const TDimension &d2) const + { + return d1.lx < d2.lx || (d1.lx == d2.lx && d1.ly < d2.ly); + } +}; + +//----------------------------------------------------------------------------- + +class OglStock +{ // singleton + + typedef std::map ContextMap; + ContextMap m_table; + + OglStock() {} + +public: + ~OglStock() + { + /* // PER ADESSO, LASCIAMO IL MEMORY LEAK DATO CHE ALTRIMENTI VA IN CRASH + ContextMap::iterator it = m_table.begin(); + for(; it!=m_table.end(); ++it) + { + delete it->second; + } + */ + } + + TOfflineGL *get(const TDimension &d) + { + ContextMap::iterator it = m_table.find(d); + if (it == m_table.end()) { + TOfflineGL *glContext; + glContext = new TOfflineGL(d); + pair result = m_table.insert(ContextMap::value_type(d, glContext)); + assert(result.second); + assert(m_table.size() < 15); + return glContext; + } + return it->second; + } + + static OglStock *instance() + { + static OglStock singleton; + return &singleton; + } +}; + +} // namespace + +//----------------------------------------------------------------------------- + +TOfflineGL *TOfflineGL::getStock(const TDimension dim) +{ + return OglStock::instance()->get(dim); +} diff --git a/toonz/sources/common/tvrender/tpalette.cpp b/toonz/sources/common/tvrender/tpalette.cpp new file mode 100644 index 0000000..c8efb78 --- /dev/null +++ b/toonz/sources/common/tvrender/tpalette.cpp @@ -0,0 +1,1186 @@ + + +// TnzCore includes +#include "tsimplecolorstyles.h" +#include "timage_io.h" +#include "tconvert.h" +#include "tvectorimage.h" +#include "tpixelutils.h" +#include "tsystem.h" +#include "tstream.h" + +// Qt includes +#include + +#include "tpalette.h" + +PERSIST_IDENTIFIER(TPalette, "palette") + +TPersistDeclarationT auxPaletteDeclaration("vectorpalette"); + +DEFINE_CLASS_CODE(TPalette, 30) + +//************************************************************************************* +// Local namespace stuff +//************************************************************************************* + +namespace +{ + +const int maxStyleIndex = 32765; + +} // namespace + +//=================================================================== +// +// TPalette::Page +// +//------------------------------------------------------------------- + +TPalette::Page::Page(wstring name) + : m_name(name), m_index(-1), m_palette(0) +{ +} + +//------------------------------------------------------------------- + +TColorStyle *TPalette::Page::getStyle(int index) const +{ + assert(m_palette); + if (0 <= index && index < getStyleCount()) + return m_palette->getStyle(m_styleIds[index]); + else + return 0; +} + +//------------------------------------------------------------------- + +int TPalette::Page::getStyleId(int index) const +{ + assert(m_palette); + if (0 <= index && index < getStyleCount()) + return m_styleIds[index]; + else + return -1; +} + +//------------------------------------------------------------------- + +int TPalette::Page::addStyle(int styleId) +{ + assert(m_palette); + if (styleId < 0 || styleId >= m_palette->getStyleCount()) + return -1; + if (m_palette->m_styles[styleId].first != 0) + return -1; + m_palette->m_styles[styleId].first = this; + int indexInPage = int(m_styleIds.size()); + m_styleIds.push_back(styleId); + return indexInPage; +} + +//------------------------------------------------------------------- + +int TPalette::Page::addStyle(TColorStyle *style) +{ + assert(m_palette); + int stylesCount = int(m_palette->m_styles.size()); + int styleId; + for (styleId = 0; styleId < stylesCount; styleId++) + if (m_palette->m_styles[styleId].first == 0) + break; + if (styleId >= stylesCount - 1) + return addStyle(m_palette->addStyle(style)); + m_palette->setStyle(styleId, style); + return addStyle(styleId); +} + +//------------------------------------------------------------------- + +int TPalette::Page::addStyle(TPixel32 color) +{ + return addStyle(new TSolidColorStyle(color)); +} + +//------------------------------------------------------------------- + +void TPalette::Page::insertStyle(int indexInPage, int styleId) +{ + assert(m_palette); + if (styleId < 0 || styleId >= m_palette->getStyleCount()) + return; + if (m_palette->m_styles[styleId].first != 0) + return; + m_palette->m_styles[styleId].first = this; + if (indexInPage < 0) + indexInPage = 0; + else if (indexInPage > getStyleCount()) + indexInPage = getStyleCount(); + m_styleIds.insert(m_styleIds.begin() + indexInPage, styleId); +} + +//------------------------------------------------------------------- + +void TPalette::Page::insertStyle(int indexInPage, TColorStyle *style) +{ + assert(m_palette); + int styleId = m_palette->addStyle(style); + if (styleId >= 0) + insertStyle(indexInPage, styleId); +} + +//------------------------------------------------------------------- + +void TPalette::Page::insertStyle(int indexInPage, TPixel32 color) +{ + assert(m_palette); + int styleId = m_palette->addStyle(color); + if (styleId >= 0) + insertStyle(indexInPage, styleId); +} + +//------------------------------------------------------------------- + +void TPalette::Page::removeStyle(int indexInPage) +{ + if (indexInPage < 0 || indexInPage >= getStyleCount()) + return; + assert(m_palette); + int styleId = getStyleId(indexInPage); + assert(0 <= styleId && styleId < m_palette->getStyleCount()); + assert(m_palette->m_styles[styleId].first == this); + m_palette->m_styles[styleId].first = 0; + m_styleIds.erase(m_styleIds.begin() + indexInPage); +} + +//------------------------------------------------------------------- + +int TPalette::Page::search(int styleId) const +{ + std::vector::const_iterator it = + std::find(m_styleIds.begin(), m_styleIds.end(), styleId); + if (it == m_styleIds.end()) + return -1; + else + return it - m_styleIds.begin(); +} + +//------------------------------------------------------------------- + +int TPalette::Page::search(TColorStyle *style) const +{ + assert(style); + assert(m_palette); + for (int i = 0; i < getStyleCount(); i++) + if (m_palette->getStyle(m_styleIds[i]) == style) + return i; + return -1; +} + +//=================================================================== +// +// TPalette +// +//------------------------------------------------------------------- + +TPalette::TPalette() + : m_version(0), m_isCleanupPalette(false), m_currentFrame(-1), m_dirtyFlag(false), m_mutex(QMutex::Recursive), m_isLocked(false), m_askOverwriteFlag(false) +{ + QString tempName(QObject::tr("colors")); + wstring pageName = tempName.toStdWString(); + Page *page = addPage(pageName); + page->addStyle(TPixel32(255, 255, 255, 0)); + page->addStyle(TPixel32(0, 0, 0, 255)); + getStyle(0)->setName(L"color_0"); + getStyle(1)->setName(L"color_1"); + + for (int i = 0; i < 10; i++) + m_shortcuts['0' + i] = i; +} + +//------------------------------------------------------------------- + +TPalette::~TPalette() +{ + std::set table; + int i = 0; + for (i = 0; i < getStyleCount(); i++) { + assert(table.find(getStyle(i)) == table.end()); + table.insert(getStyle(i)); + } + clearPointerContainer(m_pages); +} + +//------------------------------------------------------------------- + +TPalette *TPalette::clone() const +{ + TPalette *palette = new TPalette; + palette->assign(this); + return palette; +} + +//------------------------------------------------------------------- + +TColorStyle *TPalette::getStyle(int index) const +{ + if (0 <= index && index < getStyleCount()) + return m_styles[index].second.getPointer(); + else { + static TSolidColorStyle *ss = new TSolidColorStyle(TPixel32::Red); + ss->addRef(); + return ss; + } +} + +//------------------------------------------------------------------- + +int TPalette::getStyleInPagesCount() const +{ + int styleInPagesCount = 0; + for (int i = 0; i < getStyleCount(); i++) + if (m_styles[i].first != 0) + styleInPagesCount++; + return styleInPagesCount; +} + +//------------------------------------------------------------------- + +int TPalette::getFirstUnpagedStyle() const +{ + for (int i = 0; i < getStyleCount(); i++) + if (m_styles[i].first == 0) + return i; + return -1; +} + +//------------------------------------------------------------------- +/*! Adding style with new styleId. Even if there are deleted styles in the palette, the new style will be appended to the end of the list. +*/ +int TPalette::addStyle(TColorStyle *style) +{ + //limit the number of cleanup style to 7 + if (isCleanupPalette() && getStyleInPagesCount() >= 8) + return -1; + + int styleId = int(m_styles.size()); + if (styleId < 4096) { + //checking if the style is overlapped + int i = 0; + for (i = 0; i < styleId; i++) + if (getStyle(i) == style) + break; + if (i == styleId) { + m_styles.push_back(std::make_pair((Page *)0, style)); + return styleId; + } + } + delete style; + return -1; +} + +//------------------------------------------------------------------- + +int TPalette::addStyle(const TPixel32 &color) +{ + return addStyle(new TSolidColorStyle(color)); +} + +//------------------------------------------------------------------- + +void TPalette::setStyle(int styleId, TColorStyle *style) +{ + std::auto_ptr styleOwner(style); + + int styleCount = getStyleCount(); + + if (0 <= styleId && styleId < styleCount) { + // Find out if the supplied style is already in the palette + // with a different style id. In that case, bail out as a noop. + for (int i = 0; i < styleCount; ++i) + if (style == getStyle(i)) + return; + + // Substitution can take place + if (typeid(*m_styles[styleId].second.getPointer()) != typeid(*style)) + m_styleAnimationTable.erase(styleId); + + m_styles[styleId].second = styleOwner.release(); + } +} + +//------------------------------------------------------------------- + +void TPalette::setStyle(int styleId, const TPixelRGBM32 &color) +{ + setStyle(styleId, new TSolidColorStyle(color)); +} + +//------------------------------------------------------------------- + +int TPalette::getPageCount() const +{ + return int(m_pages.size()); +} + +//------------------------------------------------------------------- + +TPalette::Page *TPalette::getPage(int pageIndex) +{ + if (0 <= pageIndex && pageIndex < getPageCount()) { + Page *page = m_pages[pageIndex]; + assert(page->getIndex() == pageIndex); + assert(page->m_palette == this); + return page; + } else + return 0; +} + +//------------------------------------------------------------------- + +const TPalette::Page *TPalette::getPage(int pageIndex) const +{ + if (0 <= pageIndex && pageIndex < getPageCount()) { + Page *page = m_pages[pageIndex]; + assert(page->getIndex() == pageIndex); + assert(page->m_palette == this); + return page; + } else + return 0; +} + +//------------------------------------------------------------------- + +TPalette::Page *TPalette::addPage(wstring name) +{ + Page *page = new Page(name); + page->m_index = getPageCount(); + page->m_palette = this; + m_pages.push_back(page); + return page; +} + +//------------------------------------------------------------------- + +void TPalette::erasePage(int index) +{ + Page *page = getPage(index); + if (!page) + return; + m_pages.erase(m_pages.begin() + index); + int i; + for (i = 0; i < getPageCount(); i++) + m_pages[i]->m_index = i; + for (i = 0; i < page->getStyleCount(); i++) + m_styles[page->getStyleId(i)].first = 0; + page->m_palette = 0; + delete page; +} + +//------------------------------------------------------------------- + +void TPalette::movePage(Page *page, int dstPageIndex) +{ + assert(page); + assert(page->m_palette == this); + dstPageIndex = tcrop(dstPageIndex, 0, getPageCount() - 1); + if (dstPageIndex == page->getIndex()) + return; + m_pages.erase(m_pages.begin() + page->getIndex()); + m_pages.insert(m_pages.begin() + dstPageIndex, page); + for (int i = 0; i < getPageCount(); i++) + m_pages[i]->m_index = i; + assert(page->getIndex() == dstPageIndex); +} + +//------------------------------------------------------------------- + +TPalette::Page *TPalette::getStylePage(int styleId) const +{ + if (0 <= styleId && styleId < getStyleCount()) + return m_styles[styleId].first; + else + return 0; +} + +//------------------------------------------------------------------- + +int TPalette::getClosestStyle(const TPixel32 &color) const +{ + struct locals { + static inline int getDistance2(const TPixel32 &a, const TPixel32 &b) + { + return (a.r - b.r) * (a.r - b.r) + (a.g - b.g) * (a.g - b.g) + (a.b - b.b) * (a.b - b.b) + (a.m - b.m) * (a.m - b.m); + } + }; // locals + + if (color == TPixel32::Transparent) + return 0; + int bestIndex = -1; + int bestDistance = 255 * 255 * 4 + 1; + for (int i = 1; i < (int)m_styles.size(); i++) { + // if(i==FirstUserStyle+2) continue; + TSolidColorStyle *scs = dynamic_cast(m_styles[i].second.getPointer()); + if (scs) { + int d = locals::getDistance2(scs->getMainColor(), color); + if (d < bestDistance) { + bestIndex = i; + bestDistance = d; + } + } + } + return bestIndex; +} + +//------------------------------------------------------------------- + +bool TPalette::getFxRects(const TRect &rect, TRect &rectIn, TRect &rectOut) +{ + int i; + bool ret = false; + int borderIn, borderOut, fullBorderIn = 0, fullBorderOut = 0; + + for (i = 0; i < (int)m_styles.size(); i++) + if (m_styles[i].second->isRasterStyle()) { + m_styles[i].second->getRasterStyleFx()->getEnlargement(borderIn, borderOut); + fullBorderIn = tmax(fullBorderIn, borderIn); + fullBorderOut = tmax(fullBorderOut, borderOut); + ret = true; + } + + rectIn = rect.enlarge(fullBorderIn); + rectOut = rect.enlarge(fullBorderOut); + return ret; +} + +//=================================================================== +// +// I/O +// +//------------------------------------------------------------------- + +namespace +{ + +class StyleWriter : public TOutputStreamInterface +{ + TOStream &m_os; + int m_index; + +public: + static TFilePath m_rootDir; + StyleWriter(TOStream &os, int index) : m_os(os), m_index(index) + { + } + static void setRootDir(const TFilePath &fp) { m_rootDir = fp; } + + TOutputStreamInterface &operator<<(double x) + { + m_os << x; + return *this; + }; + TOutputStreamInterface &operator<<(int x) + { + m_os << x; + return *this; + }; + TOutputStreamInterface &operator<<(string x) + { + m_os << x; + return *this; + }; + TOutputStreamInterface &operator<<(UCHAR x) + { + m_os << (int)x; + return *this; + }; + TOutputStreamInterface &operator<<(USHORT x) + { + m_os << (int)x; + return *this; + }; + TOutputStreamInterface &operator<<(const TPixel32 &x) + { + m_os << x; + return *this; + }; + TOutputStreamInterface &operator<<(const TRaster32P &ras) + { + assert(m_rootDir != TFilePath()); + + string name = "texture_" + toString(m_index); + m_os << name; + TFilePath filename = + ((m_rootDir + "textures") + name).withType("bmp"); + if (!TFileStatus(m_rootDir + "textures").doesExist()) { + try { + TSystem::mkDir(m_rootDir + "textures"); + } catch (...) { + } + } + + TImageWriter::save(filename, ras); + return *this; + }; +}; + +//------------------------------------------------------------------- + +class StyleReader : public TInputStreamInterface +{ + TIStream &m_is; //!< Wrapped input stream. + VersionNumber m_version; //!< Palette version number (overrides m_is's one). + +public: + static TFilePath m_rootDir; + +public: + StyleReader(TIStream &is, const VersionNumber &version) + : m_is(is), m_version(version) {} + + static void setRootDir(const TFilePath &fp) { m_rootDir = fp; } + + virtual TInputStreamInterface &operator>>(double &x) + { + m_is >> x; + return *this; + } + virtual TInputStreamInterface &operator>>(int &x) + { + m_is >> x; + return *this; + } + virtual TInputStreamInterface &operator>>(string &x) + { + m_is >> x; + return *this; + } + virtual TInputStreamInterface &operator>>(UCHAR &x) + { + int v; + m_is >> v; + x = v; + return *this; + } + virtual TInputStreamInterface &operator>>(USHORT &x) + { + int v; + m_is >> v; + x = v; + return *this; + } + virtual TInputStreamInterface &operator>>(TRaster32P &x) + { + assert(m_rootDir != TFilePath()); + string name; + m_is >> name; + TFilePath filename = + ((m_rootDir + "textures") + name).withType("bmp"); + TRasterP ras; + if (TImageReader::load(filename, ras)) { + x = ras; + } + return *this; + } + virtual TInputStreamInterface &operator>>(TPixel32 &x) + { + m_is >> x; + return *this; + } + + /*! + \details Explicitly ovverrides the stream's version, returning m_version. + This is necessary since palettes have their \a own version number, + which is \a not the TIStream's file one. + */ + virtual VersionNumber versionNumber() const + { + return m_version; + } //!< Returns the palette's version number. +}; + +TFilePath StyleWriter::m_rootDir = TFilePath(); +TFilePath StyleReader::m_rootDir = TFilePath(); + +} // namespace + +//=================================================================== + +void TPalette::setRootDir(const TFilePath &fp) +{ + StyleWriter::setRootDir(fp); + StyleReader::setRootDir(fp); +} + +//------------------------------------------------------------------- + +void TPalette::saveData(TOStream &os) +{ + os.child("version") << 71 << 0; // Inserting the version tag at this level. + // This is necessary to support the tpl format + if (m_refImgPath != TFilePath()) // since it performs *untagged* stream output + os.child("refImgPath") << m_refImgPath; // (the palette is streamed directly). + + os.openChild("styles"); + { + for (int i = 0; i < getStyleCount(); ++i) { + os.openChild("style"); + { + StyleWriter w(os, i); + m_styles[i].second->save(w); + } + os.closeChild(); + } + } + os.closeChild(); + + os.openChild("stylepages"); + { + for (int i = 0; i < getPageCount(); ++i) { + Page *page = getPage(i); + os.openChild("page"); + { + os.child("name") << page->getName(); + + os.openChild("indices"); + { + int m = page->getStyleCount(); + + for (int j = 0; j < m; ++j) + os << page->getStyleId(j); + } + os.closeChild(); + } + os.closeChild(); + } + } + os.closeChild(); + + if (isAnimated()) { + os.openChild("animation"); + { + StyleAnimationTable::iterator sat, saEnd = m_styleAnimationTable.end(); + for (sat = m_styleAnimationTable.begin(); sat != saEnd; ++sat) { + int styleId = sat->first; + StyleAnimation &animation = sat->second; + + std::map attributes; + attributes["id"] = toString(styleId); + + os.openChild("style", attributes); + { + StyleAnimation::iterator kt, kEnd = animation.end(); + for (kt = animation.begin(); kt != kEnd; ++kt) { + int frame = kt->first; + + TColorStyle *cs = kt->second.getPointer(); + assert(cs); + + attributes.clear(); + attributes["frame"] = toString(frame); + + /*os.openChild("keycolor", attributes); // Up to Toonz 7.0, animations saved + os << cs->getMainColor(); // the main color only + os.closeChild();*/ // + + os.openChild("keyframe", attributes); + { + StyleWriter w(os, sat->first); + kt->second->save(w); + } + os.closeChild(); + } + } + os.closeChild(); + } + } + os.closeChild(); + } + + // salvo gli shortcuts solo se sono non standard + int i; + for (i = 0; i < 10; ++i) + if (getShortcutValue('0' + i) != i) + break; + + if (i < 10) { + os.openChild("shortcuts"); + { + for (i = 0; i < 10; i++) + os << getShortcutValue('0' + i); + } + os.closeChild(); + } + if (isLocked()) { + os.openChild("lock"); + os << 1; + os.closeChild(); + } +} + +//------------------------------------------------------------------- + +void TPalette::loadData(TIStream &is) +{ + m_styles.clear(); + clearPointerContainer(m_pages); + + VersionNumber version = is.getVersion(); + + string tagName; + while (is.openChild(tagName)) { + if (tagName == "version") { + is >> version.first >> version.second; + if (version > VersionNumber(71, 0)) + throw TException("palette, unsupported version number"); + } else if (tagName == "styles") { + while (!is.eos()) // I think while(is.openChild(tagName)) + { // would be better. However, I don't trust + if (!is.openChild(tagName) || tagName != "style") // TIStream's implementation very much. Keeping it + throw TException("palette, expected tag