diff --git a/Blocks3D.vcproj b/Blocks3D.vcproj index ebaf57d..e67dd6c 100644 --- a/Blocks3D.vcproj +++ b/Blocks3D.vcproj @@ -670,6 +670,14 @@ > + + + + + + + + ThumbnailGeneratorInstance::ThumbnailGeneratorInstance(void) @@ -13,39 +14,37 @@ ThumbnailGeneratorInstance::ThumbnailGeneratorInstance(void) ThumbnailGeneratorInstance::~ThumbnailGeneratorInstance(void) {} +/* + * TODO: + * Move functions like toggleSky into their own "Lighting" instance + * Make this headless, and allow for resolutions greater than the screen resolution +*/ std::string ThumbnailGeneratorInstance::click(std::string fileType, int cx, int cy, bool hideSky) { - // Important things we need RenderDevice* rd = g_usableApp->getRenderDevice(); GuiRootInstance* guiRoot = g_dataModel->getGuiRoot(); - const G3D::GImage::Format format = G3D::GImage::stringToFormat(fileType); - - // Hide the GUI - guiRoot->hideGui(true); + int prevWidth = rd->width(); + int prevHeight = rd->height(); + G3D::GImage imgBuffer(cx, cy, 4); + G3D::BinaryOutput binOut; + + guiRoot->hideGui(true); + g_usableApp->resize3DView(cx, cy); - // Disable the sky if(hideSky) g_usableApp->toggleSky(); - // Update graphics g_usableApp->onGraphics(rd); - - // Sky SHOULD be gone now, and alpha channel should be present - G3D::GImage imgBuffer(cx, cy, 4); rd->screenshotPic(imgBuffer, true, hideSky); - - G3D::BinaryOutput binOut; imgBuffer.encode(format, binOut); - // Temporary file saving - std::string fileSave = "./click_output." + fileType; - std::ofstream out(fileSave.c_str(), std::ios::out | std::ios::binary); - out.write(reinterpret_cast(binOut.getCArray()), binOut.length()); - - // Unhide GUI + // Convert to Base64 string + std::string base64ImgStr = base64_encode(reinterpret_cast(binOut.getCArray()), binOut.length(), false); + guiRoot->hideGui(false); + g_usableApp->resize3DView(prevWidth, prevHeight); - return "boop!"; + return base64ImgStr; } diff --git a/click_output.PNG b/click_output.PNG index eaa12a9..18cdea8 100644 Binary files a/click_output.PNG and b/click_output.PNG differ diff --git a/src/include/Application.h b/src/include/Application.h index 0921f96..040c468 100644 --- a/src/include/Application.h +++ b/src/include/Application.h @@ -48,16 +48,18 @@ class Application { // : public GApp { void setFocus(bool isFocused); int getMode(); void unSetMode(); + void toggleSky(); CameraController cameraController; UserInput* userInput; PropertyWindow* _propWindow; - void generateShadowMap(const CoordinateFrame& lightViewMatrix) const; + void generateShadowMap(const CoordinateFrame& lightViewMatrix) const; RenderDevice* getRenderDevice(); void selectInstance(Instance* selectedInstance,PropertyWindow* propWindow); void setMode(int mode); - SkyRef getSky(); - + SkyRef getSky(); + void resize3DView(int w, int h); + Tool * tool; void changeTool(Tool *); Mouse mouse; diff --git a/src/include/base64.h b/src/include/base64.h new file mode 100644 index 0000000..866505e --- /dev/null +++ b/src/include/base64.h @@ -0,0 +1,35 @@ +// +// base64 encoding and decoding with C++. +// Version: 2.rc.08 (release candidate) +// + +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A + +#include + +#if __cplusplus >= 201703L +#include +#endif // __cplusplus >= 201703L + +std::string base64_encode (std::string const& s, bool url = false); +std::string base64_encode_pem (std::string const& s); +std::string base64_encode_mime(std::string const& s); + +std::string base64_decode(std::string const& s, bool remove_linebreaks = false); +std::string base64_encode(unsigned char const*, size_t len, bool url = false); + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// +std::string base64_encode (std::string_view s, bool url = false); +std::string base64_encode_pem (std::string_view s); +std::string base64_encode_mime(std::string_view s); + +std::string base64_decode(std::string_view s, bool remove_linebreaks = false); +#endif // __cplusplus >= 201703L + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/src/source/Application.cpp b/src/source/Application.cpp index eece56f..a0121bf 100644 --- a/src/source/Application.cpp +++ b/src/source/Application.cpp @@ -111,6 +111,8 @@ Application::Application(HWND parentWindow) : _propWindow(NULL) { //: GApp(setti _settings.writeLicenseFile = false; _settings.logFilename = tempPath + "/g3dlog.txt"; _settings.window.center = true; + _settings.window.fsaaSamples = 8; + Win32Window* window = Win32Window::create(_settings.window,_hwndRenderer); ShowWindow(_hwndRenderer, SW_SHOW); ShowWindow(_hWndMain, SW_SHOW); @@ -868,6 +870,7 @@ void Application::resizeWithParent(HWND parentWindow) } +// These should be moved into a "Lighting" class G3D::SkyRef Application::getSky() { return sky; @@ -878,6 +881,13 @@ void Application::toggleSky() _hideSky = !_hideSky; } +void Application::resize3DView(int w, int h) +{ + Rect2D newViewport = Rect2D::xywh(0, 0, w, h); + renderDevice->notifyResize(w, h); + renderDevice->setViewport(newViewport); +} + void Application::QuitApp() { PostQuitMessage(0); diff --git a/src/source/Listener/MenuButtonListener.cpp b/src/source/Listener/MenuButtonListener.cpp index 96add33..4e8575f 100644 --- a/src/source/Listener/MenuButtonListener.cpp +++ b/src/source/Listener/MenuButtonListener.cpp @@ -18,8 +18,8 @@ void MenuButtonListener::onButton1MouseClick(BaseButtonInstance* button) AppendMenu(mainmenu, MF_SEPARATOR, 0, NULL); // Temporary - AppendMenu(mainmenu, MF_STRING, 103, "ThumbnailGenerator::click hideSky = true"); - AppendMenu(mainmenu, MF_STRING, 104, "ThumbnailGenerator::click hideSky = false"); + // AppendMenu(mainmenu, MF_STRING, 103, "ThumbnailGenerator::click hideSky = true"); + // AppendMenu(mainmenu, MF_STRING, 104, "ThumbnailGenerator::click hideSky = false"); POINT p; GetCursorPos(&p); @@ -36,12 +36,17 @@ void MenuButtonListener::onButton1MouseClick(BaseButtonInstance* button) case 102: g_usableApp->QuitApp(); break; + + /* + // These are only here for future reference case 103: g_dataModel->getThumbnailGenerator()->click("PNG", 256, 256, true); break; case 104: - g_dataModel->getThumbnailGenerator()->click("JPEG", 256, 256, false); + g_dataModel->getThumbnailGenerator()->click("JPEG", 105, 70, false); break; + */ + } } } \ No newline at end of file diff --git a/src/source/base64.cpp b/src/source/base64.cpp new file mode 100644 index 0000000..7666ef8 --- /dev/null +++ b/src/source/base64.cpp @@ -0,0 +1,282 @@ +/* + base64.cpp and base64.h + + base64 encoding and decoding with C++. + More information at + https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp + + Version: 2.rc.08 (release candidate) + + Copyright (C) 2004-2017, 2020, 2021 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" + +#include +#include + + // + // Depending on the url parameter in base64_chars, one of + // two sets of base64 characters needs to be chosen. + // They differ in their last two characters. + // +static const char* base64_chars[2] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/", + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-_"}; + +static unsigned int pos_of_char(const unsigned char chr) { + // + // Return the position of chr within base64_encode() + // + + if (chr >= 'A' && chr <= 'Z') return chr - 'A'; + else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; + else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; + else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( + else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' + else + // + // 2020-10-23: Throw std::exception rather than const char* + //(Pablo Martin-Gomez, https://github.com/Bouska) + // + throw std::runtime_error("Input is not valid base64-encoded data."); +} + +static std::string insert_linebreaks(std::string str, size_t distance) { + // + // Provided by https://github.com/JomaCorpFX, adapted by me. + // + if (!str.length()) { + return ""; + } + + size_t pos = distance; + + while (pos < str.size()) { + str.insert(pos, "\n"); + pos += distance + 1; + } + + return str; +} + +template +static std::string encode_with_line_breaks(String s) { + return insert_linebreaks(base64_encode(s, false), line_length); +} + +template +static std::string encode_pem(String s) { + return encode_with_line_breaks(s); +} + +template +static std::string encode_mime(String s) { + return encode_with_line_breaks(s); +} + +template +static std::string encode(String s, bool url) { + return base64_encode(reinterpret_cast(s.data()), s.length(), url); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url) { + + size_t len_encoded = (in_len +2) / 3 * 4; + + unsigned char trailing_char = url ? '.' : '='; + + // + // Choose set of base64 characters. They differ + // for the last two positions, depending on the url + // parameter. + // A bool (as is the parameter url) is guaranteed + // to evaluate to either 0 or 1 in C++ therefore, + // the correct character set is chosen by subscripting + // base64_chars with url. + // + const char* base64_chars_ = base64_chars[url]; + + std::string ret; + ret.reserve(len_encoded); + + unsigned int pos = 0; + + while (pos < in_len) { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); + + if (pos+1 < in_len) { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); + + if (pos+2 < in_len) { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); + ret.push_back(base64_chars_[ bytes_to_encode[pos + 2] & 0x3f]); + } + else { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]); + ret.push_back(trailing_char); + } + } + else { + + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]); + ret.push_back(trailing_char); + ret.push_back(trailing_char); + } + + pos += 3; + } + + + return ret; +} + +template +static std::string decode(String encoded_string, bool remove_linebreaks) { + // + // decode(…) is templated so that it can be used with String = const std::string& + // or std::string_view (requires at least C++17) + // + + if (encoded_string.empty()) return std::string(); + + if (remove_linebreaks) { + + std::string copy(encoded_string); + + copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end()); + + return base64_decode(copy, false); + } + + size_t length_of_string = encoded_string.length(); + size_t pos = 0; + + // + // The approximate length (bytes) of the decoded string might be one or + // two bytes smaller, depending on the amount of trailing equal signs + // in the encoded string. This approximation is needed to reserve + // enough space in the string to be returned. + // + size_t approx_length_of_decoded_string = length_of_string / 4 * 3; + std::string ret; + ret.reserve(approx_length_of_decoded_string); + + while (pos < length_of_string) { + // + // Iterate over encoded input string in chunks. The size of all + // chunks except the last one is 4 bytes. + // + // The last chunk might be padded with equal signs or dots + // in order to make it 4 bytes in size as well, but this + // is not required as per RFC 2045. + // + // All chunks except the last one produce three output bytes. + // + // The last chunk produces at least one and up to three bytes. + // + + size_t pos_of_char_1 = pos_of_char(encoded_string[pos+1] ); + + // + // Emit the first output byte that is produced in each chunk: + // + ret.push_back(static_cast( ( (pos_of_char(encoded_string[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4))); + + if ( ( pos + 2 < length_of_string ) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045) + encoded_string[pos+2] != '=' && + encoded_string[pos+2] != '.' // accept URL-safe base 64 strings, too, so check for '.' also. + ) + { + // + // Emit a chunk's second byte (which might not be produced in the last chunk). + // + unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos+2] ); + ret.push_back(static_cast( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2))); + + if ( ( pos + 3 < length_of_string ) && + encoded_string[pos+3] != '=' && + encoded_string[pos+3] != '.' + ) + { + // + // Emit a chunk's third byte (which might not be produced in the last chunk). + // + ret.push_back(static_cast( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(encoded_string[pos+3]) )); + } + } + + pos += 4; + } + + return ret; +} + +std::string base64_decode(std::string const& s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + +std::string base64_encode(std::string const& s, bool url) { + return encode(s, url); +} + +std::string base64_encode_pem (std::string const& s) { + return encode_pem(s); +} + +std::string base64_encode_mime(std::string const& s) { + return encode_mime(s); +} + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// + +std::string base64_encode(std::string_view s, bool url) { + return encode(s, url); +} + +std::string base64_encode_pem(std::string_view s) { + return encode_pem(s); +} + +std::string base64_encode_mime(std::string_view s) { + return encode_mime(s); +} + +std::string base64_decode(std::string_view s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + +#endif // __cplusplus >= 201703L