diff options
Diffstat (limited to 'libraries/daguerre/include')
-rw-r--r-- | libraries/daguerre/include/daguerre.hpp | 209 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/fixed-font.hpp | 57 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/framebuffer.hpp | 9 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/general.hpp | 59 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/image.hpp | 118 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/impl/fixed-font.hpp | 59 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/impl/image.hpp | 217 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/ppm.hpp | 20 | ||||
-rw-r--r-- | libraries/daguerre/include/daguerre/psf.hpp | 20 |
9 files changed, 559 insertions, 209 deletions
diff --git a/libraries/daguerre/include/daguerre.hpp b/libraries/daguerre/include/daguerre.hpp deleted file mode 100644 index 9468826..0000000 --- a/libraries/daguerre/include/daguerre.hpp +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once - -#include <algorithm> -#include <stdint.h> -#include <cstring> -#include <cstdio> - -namespace daguerre { - - typedef uint32_t hilbert_color; - - struct rgb24 { - uint8_t r; - uint8_t g; - uint8_t b; - }; - - template <class color_t> - static inline void default_overlay(color_t &dest, const color_t &src) { - dest = src; - } - - static inline void default_overlay(hilbert_color &dest, const rgb24 &src) { - dest = __euler_encode_color(src.r, src.g, src.b); - } - - static inline void default_overlay(rgb24 &dest, const bool &src) { - dest.r = src ? 255 : 0; - dest.g = src ? 255 : 0; - dest.b = src ? 255 : 0; - } - - template <class color_t> - class image { - - public: - bool delete_buffer_on_destruct; - color_t *buffer; - unsigned width; - unsigned height; - unsigned pitch;//in sizeof(color_t) - - image() - : delete_buffer_on_destruct(false), buffer(0), width(0), height(0), - pitch(0) {} - - image(unsigned width, unsigned height) - : delete_buffer_on_destruct(true), buffer(new color_t[width * height]), - width(width), height(height), pitch(width) {} - - image( - color_t *buffer, unsigned width, unsigned height, unsigned pitch, - bool delete_buffer_on_destruct) - : delete_buffer_on_destruct(delete_buffer_on_destruct), buffer(buffer), - width(width), height(height), pitch(pitch) {} - - ~image() { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - image(const image<other_color_t> &other) - : delete_buffer_on_destruct(true), - buffer(new color_t[other.width * other.height]), width(other.width), - height(other.height), pitch(other.width) { - overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height); - } - - image(image<color_t> &&other) - : delete_buffer_on_destruct(other.delete_buffer_on_destruct), - buffer(other.buffer), width(other.width), height(other.height), - pitch(other.pitch) { - other.buffer = 0; - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - image<color_t> &operator =(const image<other_color_t> &other) { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - delete_buffer_on_destruct = true; - width = other.width; - height = other.height; - pitch = width; - buffer = new color_t[width * height]; - overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height); - return *this; - } - - image<color_t> &operator =(image<color_t> &&other) { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - delete_buffer_on_destruct = other.delete_buffer_on_destruct; - buffer = other.buffer; - width = other.width; - height = other.height; - pitch = other.pitch; - other.buffer = 0; - return *this; - } - - color_t get(unsigned x, unsigned y) const { - return buffer[y * pitch + x]; - } - - void set(unsigned x, unsigned y, const color_t &c) { - buffer[y * pitch + x] = c; - } - - void copy_from( - const image<color_t> &other, unsigned to_x, unsigned to_y, - unsigned from_x, unsigned from_y, unsigned width, unsigned height) { - - color_t *to_start = buffer + pitch * to_y + to_x; - const color_t *from_start = other.buffer + other.pitch * from_y + from_x; - - for (unsigned y = 0; y < height; ++y) - std::memcpy( - to_start + pitch * y, from_start + other.pitch * y, - width * sizeof(color_t)); - - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - void overlay_from( - const image<other_color_t> &other, unsigned to_x, unsigned to_y, - unsigned from_x, unsigned from_y, unsigned width, unsigned height) { - - color_t *to_start = buffer + pitch * to_y + to_x; - const other_color_t *from_start = - other.buffer + other.pitch * from_y + from_x; - - for (unsigned y = 0; y < height; ++y) - for (unsigned x = 0; x < width; ++x) - overlay(to_start[pitch * y + x], from_start[other.pitch * y + x]); - - } - - }; - - template <class color_t> - void swap(image<color_t> &a, image<color_t> &b) { - std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct); - std::swap(a.buffer, b.buffer); - std::swap(a.width, b.width); - std::swap(a.height, b.height); - std::swap(a.pitch, b.pitch); - } - - image<hilbert_color> get_hilbert_framebuffer(); - - bool try_load_ppm(std::FILE *input, image<rgb24> &into); - - static inline bool try_load_ppm(const char *path, image<rgb24> &into) { - std::FILE *f = std::fopen(path, "r"); - if (!f) - return false; - bool success = try_load_ppm(f, into); - std::fclose(f); - return success; - } - - //TODO: unicode - template <class color_t> - class fixed_bitmap_font { - - public: - unsigned width; - unsigned height; - image<color_t> glyphs[128]; - - template <class target_color_t, - void overlay(target_color_t &dest, const color_t &src) = default_overlay> - void overlay_text( - image<target_color_t> &target, unsigned x, - unsigned y, const char *text) const { - - while (1) { - uint8_t ch = (uint8_t)*text; - if (ch == 0) - return; - if (ch < 128) { - target.template overlay_from<color_t, overlay>( - glyphs[ch], x, y, 0, 0, width, height); - x += width; - } - ++text; - } - - } - - }; - - bool try_load_psf(std::FILE *input, fixed_bitmap_font<bool> &into); - - static inline bool try_load_psf( - const char *path, fixed_bitmap_font<bool> &into) { - std::FILE *f = std::fopen(path, "r"); - if (!f) - return false; - bool success = try_load_psf(f, into); - std::fclose(f); - return success; - } - -} diff --git a/libraries/daguerre/include/daguerre/fixed-font.hpp b/libraries/daguerre/include/daguerre/fixed-font.hpp new file mode 100644 index 0000000..471163a --- /dev/null +++ b/libraries/daguerre/include/daguerre/fixed-font.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include <daguerre/general.hpp> + +//TODO: support more than just ascii + +namespace daguerre { + + //forward declare since these depend on each other + template <class color_t> + class image; + + template <class color_t> + class fixed_font { + + public: + int glyph_width; + int glyph_height; + image<color_t> glyphs[128]; + + //initializes every glyph to an empty image + fixed_font(); + + //initializes each glyph to have the right dimensions and + //a valid buffer, but does nothing to their contents. + fixed_font(int glyph_width, int glyph_height); + + //moves the other font's glyphs here, then + //sets the other font to have empty images. + fixed_font(fixed_font<color_t> &&other); + + //moves the other font's glyphs here, then + //sets the other font to have empty images. + fixed_font<color_t> &operator =(fixed_font<color_t> &&other); + + //use one of the two constructors below instead + fixed_font(const fixed_font<color_t> &other) = delete; + + //copies the other font's images here, passing them through conversion. + template <class other_color_t> + fixed_font(const fixed_font<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //directly calls memcpy on the images' buffers. the second argument + //does nothing except distinguish this from the other copy constructor. + fixed_font(const fixed_font<color_t> &other, bool); + + //use copy constructor and move assignment instead + fixed_font<color_t> &operator =(const fixed_font<color_t> &other) = delete; + + ~fixed_font() = default; + + }; + +} + +#include <daguerre/impl/fixed-font.hpp> diff --git a/libraries/daguerre/include/daguerre/framebuffer.hpp b/libraries/daguerre/include/daguerre/framebuffer.hpp new file mode 100644 index 0000000..c9a5226 --- /dev/null +++ b/libraries/daguerre/include/daguerre/framebuffer.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include <daguerre/image.hpp> + +namespace daguerre { + + image<hilbert_color> get_hilbert_framebuffer(); + +} diff --git a/libraries/daguerre/include/daguerre/general.hpp b/libraries/daguerre/include/daguerre/general.hpp new file mode 100644 index 0000000..78c6919 --- /dev/null +++ b/libraries/daguerre/include/daguerre/general.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <euler/syscall.hpp> +#include <stdint.h> + +namespace daguerre { + + typedef euler::syscall::encoded_color hilbert_color; + + struct rgb24 { + uint8_t r; + uint8_t g; + uint8_t b; + }; + + template <class dest_t, class src_t> + using converter_t = void (dest_t &, const src_t &); + + //copies src to dest + template <class color_t> + static inline void default_conversion(color_t &dest, const color_t &src) { + dest = src; + } + + //encodes src and copies that to dest + static inline void default_conversion(hilbert_color &dest, const rgb24 &src) { + dest = euler::syscall::encode_color(src.r, src.g, src.b); + } + + template <class dest_t, class src_t, class param_t> + using param_converter_t = void (dest_t &, const src_t &, const param_t &); + + //if src is true, copies param to dest. + //if src is false, does nothing. + template <class color_t> + static inline void default_conversion( + color_t &dest, const bool &src, const color_t ¶m) { + if (src) + dest = param; + } + + //intersects [to, to + length) with [min, max) at stores the result in + //[to, to + length). if anything was removed from either end, the same + //amount is removed from the same end(s) of [from, from + length) + static inline void make_safe( + int &to, int &from, int &length, int min, int max) { + + if (to < min) { + from += min - to; + length -= min - to; + to = min; + } + + if (to + length > max) + length = max - to; + + } + +} diff --git a/libraries/daguerre/include/daguerre/image.hpp b/libraries/daguerre/include/daguerre/image.hpp new file mode 100644 index 0000000..4c44dd0 --- /dev/null +++ b/libraries/daguerre/include/daguerre/image.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include <daguerre/general.hpp> + +namespace daguerre { + + //forward declare since these depend on each other + template <class color_t> + class fixed_font; + + template <class color_t> + class image { + + public: + bool delete_buffer_on_destruct; + int width; + int height; + color_t *buffer; + int buffer_pitch;//in sizeof(color_t) + + //makes empty image + image(); + + //make image with specified dimensions, contents uninitialized + image(int width, int height); + + //make image with specified dimensions and buffer + image( + int width, int height, color_t *buffer, + int buffer_pitch, bool delete_buffer_on_destruct); + + //moves the other image's data here, and + //then sets the other image to an empty one. + image(image<color_t> &&other); + + //moves the other image's data here, and + //then sets the other image to an empty one. + image<color_t> &operator =(image<color_t> &&other); + + //use one of the two constructors below instead + image(const image<color_t> &other) = delete; + + //copies the other image's data here, passing it through conversion + template <class other_color_t> + image(const image<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //directly calls memcpy on the buffer. the second argument does + //nothing except distinguish this from the other copy constructor. + image(const image<color_t> &other, bool); + + //use copy constructor and move assignment instead + image<color_t> &operator =(const image<color_t> &other) = delete; + + ~image(); + + void fill(const color_t &color); + + //does not check bounds + color_t &at(int x, int y); + + //does not check bounds + const color_t &at(int x, int y) const; + + image<color_t> stretch(int new_width, int new_height); + + //copies the region here via memcpy. does not check bounds. + void copy_from( + const image<color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height); + + //copies the entire image here via memcpy. does not check bounds. + void copy_from(const image<color_t> &other, int to_x, int to_y); + + //copies the region here through conversion. does not check bounds. + template <class other_color_t> + void convert_from( + const image<other_color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //copies the entire image here through conversion. does not check bounds. + template <class other_color_t> + void convert_from( + const image<other_color_t> &other, int to_x, int to_y, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //copies the region here through conversion. does not check bounds. + template <class other_color_t, class param_t> + void convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, int from_x, int from_y, int width, int height, + param_converter_t<color_t, other_color_t, param_t> *conversion = + &default_conversion); + + //copies the entire image here through conversion. does not check bounds. + template <class other_color_t, class param_t> + void convert_from(const param_t ¶m, + const image<other_color_t> &other, int to_x, int to_y, + param_converter_t<color_t, other_color_t, param_t> *conversion = + &default_conversion); + + //does not check bounds or wrap text + template <class font_color_t, class param_t> + void render_text( + const fixed_font<font_color_t> &font, + const param_t ¶rm, int x, int y, const char *text, + param_converter_t<color_t, font_color_t, param_t> *conversion = + &default_conversion); + + }; + + template <class color_t> + void swap(image<color_t> &a, image<color_t> &b); + +} + +#include <daguerre/impl/image.hpp> diff --git a/libraries/daguerre/include/daguerre/impl/fixed-font.hpp b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp new file mode 100644 index 0000000..dbff798 --- /dev/null +++ b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp @@ -0,0 +1,59 @@ +#pragma once + +//this file should only be included from <daguerre/fixed-font.hpp> + +#include <daguerre/image.hpp> +#include <utility> + +namespace daguerre { + + template<class color_t> + fixed_font<color_t>::fixed_font() : glyph_width(0), glyph_height(0) {} + + template <class color_t> + fixed_font<color_t>::fixed_font(int glyph_width, int glyph_height) + : glyph_width(glyph_width), glyph_height(glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(glyph_width, glyph_height); + } + + template <class color_t> + fixed_font<color_t>::fixed_font(fixed_font<color_t> &&other) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; 0 < 128; ++i) + glyphs[i] = std::move(other.glyphs[i]); + other.glyph_width = 0; + other.glyph_height = 0; + } + + template <class color_t> + fixed_font<color_t> &fixed_font<color_t>::operator =( + fixed_font<color_t> &&other) { + glyph_width = other.glyph_width; + glyph_height = other.glyph_height; + for (int i = 0; 0 < 128; ++i) + glyphs[i] = std::move(other.glyphs[i]); + other.glyph_width = 0; + other.glyph_height = 0; + } + + template <class color_t> + template <class other_color_t> + fixed_font<color_t>::fixed_font( + const fixed_font<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(other.glyphs[i], conversion); + } + + template <class color_t> + fixed_font<color_t>::fixed_font(const fixed_font<color_t> &other, bool) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(other.glyphs[i], true); + } + +} + +#include <daguerre/image.hpp> diff --git a/libraries/daguerre/include/daguerre/impl/image.hpp b/libraries/daguerre/include/daguerre/impl/image.hpp new file mode 100644 index 0000000..1264879 --- /dev/null +++ b/libraries/daguerre/include/daguerre/impl/image.hpp @@ -0,0 +1,217 @@ +#pragma once + +//this file should only be included from <daguerre/image.hpp> + +#include <daguerre/fixed-font.hpp> +#include <algorithm> +#include <cstring> + +namespace daguerre { + + template <class color_t> + image<color_t>::image() + : delete_buffer_on_destruct(false), width(0), + height(0), buffer(0), buffer_pitch(0) {} + + template <class color_t> + image<color_t>::image(int width, int height) + : delete_buffer_on_destruct(true), width(width), height(height), + buffer(new color_t[width * height]), buffer_pitch(width) {} + + template <class color_t> + image<color_t>::image( + int width, int height, color_t *buffer, + int buffer_pitch, bool delete_buffer_on_destruct) + : delete_buffer_on_destruct(delete_buffer_on_destruct), width(width), + height(height), buffer(buffer), buffer_pitch(buffer_pitch) {} + + template <class color_t> + image<color_t>::image(image<color_t> &&other) + : delete_buffer_on_destruct(other.delete_buffer_on_destruct), + width(other.width), height(other.height), buffer(other.buffer), + buffer_pitch(other.buffer_pitch) { + + other.width = 0; + other.height = 0; + other.buffer = 0; + + } + + template<class color_t> + image<color_t> &image<color_t>::operator =(image<color_t> &&other) { + + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + + delete_buffer_on_destruct = other.delete_buffer_on_destruct; + width = other.width; + height = other.height; + buffer = other.buffer; + buffer_pitch = other.buffer_pitch; + + other.width = 0; + other.height = 0; + other.buffer = 0; + + return *this; + + } + + template <class color_t> + template <class other_color_t> + image<color_t>::image(const image<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion) + : delete_buffer_on_destruct(true), width(other.width), + height(other.height), buffer(new color_t[other.width * other.height]), + buffer_pitch(other.width) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion( + buffer[y * buffer_pitch + x], + other.buffer[y * other.buffer_pitch + x]); + + } + + template <class color_t> + image<color_t>::image(const image<color_t> &other, bool) + : delete_buffer_on_destruct(true), width(width), height(other.height), + buffer(new color_t[other.width * other.height]), + buffer_pitch(other.width) { + + if (buffer_pitch == other.buffer_pitch) + memcpy(buffer, other.buffer, (height - 1) * buffer_pitch + width); + + else + for (int y = 0; y < height; ++y) + memcpy(buffer + y * buffer_pitch, + other.buffer + y * other.buffer_pitch, + sizeof(color_t) * width); + + } + + template <class color_t> + image<color_t>::~image() { + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + } + + template <class color_t> + void image<color_t>::fill(const color_t &color) { + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + buffer[y * buffer_pitch + x] = color; + } + + template <class color_t> + color_t &image<color_t>::at(int x, int y) { + return buffer[y * buffer_pitch + x]; + } + + template <class color_t> + const color_t &image<color_t>::at(int x, int y) const { + return buffer[y * buffer_pitch + x]; + } + + template <class color_t> + image<color_t> image<color_t>::stretch(int new_width, int new_height) { + image<color_t> im(new_width, new_height); + for (int y = 0; y < new_height; ++y) + for (int x = 0; x < new_width; ++x) + im.at(x, y) = at(x * width / new_width, y * height / new_height); + return im; + } + + template <class color_t> + void image<color_t>::copy_from( + const image<color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height) { + + color_t *to = buffer + to_y * buffer_pitch + to_x; + const color_t *from = other.buffer + from_y * other.buffer_pitch + from_x; + + for (int y = 0; y < height; ++y) + memcpy( + to + y * buffer_pitch, + from + y * other.buffer_pitch, + width * sizeof(color_t)); + + } + + template <class color_t> + void image<color_t>::copy_from( + const image<color_t> &other, int to_x, int to_y) { + copy_from(other, to_x, to_y, 0, 0, other.width, other.height); + } + + template <class color_t> + template <class other_color_t> + void image<color_t>::convert_from( + const image<other_color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height, + converter_t<color_t, other_color_t> *conversion) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion(at(to_x + x, to_y + y), other.at(from_x + x, from_y + y)); + + } + + template <class color_t> + template <class other_color_t> + void image<color_t>::convert_from( + const image<other_color_t> &other, int to_x, int to_y, + converter_t<color_t, other_color_t> *conversion) { + convert_from(other, to_x, to_y, 0, 0, other.width, other.y, conversion); + } + + template <class color_t> + template <class other_color_t, class param_t> + void image<color_t>::convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, int from_x, int from_y, int width, int height, + param_converter_t<color_t, other_color_t, param_t> *conversion) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion( + at(to_x + x, to_y + y), other.at(from_x + x, from_y + y), param); + + } + + template <class color_t> + template <class other_color_t, class param_t> + void image<color_t>::convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, param_converter_t<color_t, other_color_t, param_t> *conversion) { + convert_from( + param, other, to_x, to_y, 0, 0, other.width, other.y, conversion); + } + + template <class color_t> + template <class font_color_t, class param_t> + void image<color_t>::render_text( + const fixed_font<font_color_t> &font, + const param_t ¶m, int x, int y, const char *text, + param_converter_t<color_t, font_color_t, param_t> *conversion) { + + while (*text) { + int ch = *text; + if (ch >= 0 && ch < 128) + convert_from(param, font.glyphs[ch], x, y, conversion); + ++text; + x += font.glyph_width; + } + + } + + template <class color_t> + void swap(image<color_t> &a, image<color_t> &b) { + std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct); + std::swap(a.width, b.width); + std::swap(a.height, b.height); + std::swap(a.buffer, b.buffer); + std::swap(a.buffer_pitch, b.buffer_pitch); + } + +} diff --git a/libraries/daguerre/include/daguerre/ppm.hpp b/libraries/daguerre/include/daguerre/ppm.hpp new file mode 100644 index 0000000..5249c98 --- /dev/null +++ b/libraries/daguerre/include/daguerre/ppm.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <daguerre/image.hpp> +#include <optional> +#include <cstdio> + +namespace daguerre { + + std::optional<image<rgb24>> try_load_ppm(FILE *input); + + static inline std::optional<image<rgb24>> try_load_ppm(const char *path) { + FILE *f = fopen(path, "r"); + if (!f) + return {}; + auto result = try_load_ppm(f); + fclose(f); + return result; + } + +} diff --git a/libraries/daguerre/include/daguerre/psf.hpp b/libraries/daguerre/include/daguerre/psf.hpp new file mode 100644 index 0000000..e38cdc4 --- /dev/null +++ b/libraries/daguerre/include/daguerre/psf.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <daguerre/fixed-font.hpp> +#include <optional> +#include <cstdio> + +namespace daguerre { + + std::optional<fixed_font<bool>> try_load_psf(FILE *input); + + static inline std::optional<fixed_font<bool>> try_load_psf(const char *path) { + FILE *f = fopen(path, "r"); + if (!f) + return {}; + auto result = try_load_psf(f); + fclose(f); + return result; + } + +} |