// gsclib.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include "stdlib.h" #include "stdint.h" #include "stdio.h" #include <assert.h> #include "gsclib.h" struct FractalParams { COLORREF colors[16]; int expand; // palette expansion factor: <1, 64>; 1 - use basic 16 colors only, 64 - use 16 x 64 shades int step; // RGB step for subsequent expaned color double zr, zi; double xmin, xmax; double ymin, ymax; }; COLORREF defaultColors[] = { RGB(0, 255, 0), // Green RGB(255, 255, 0), // Yellow RGB(255, 0, 0), // Red RGB(0, 0, 255), // Blue RGB(0, 255, 255), // Cyan RGB(128, 128, 128), // Grey RGB(0, 128, 128), // Aqua RGB(255, 0, 255), // Magenta RGB(128, 128, 0), // Brown RGB(128, 0, 128), // Purple RGB(255, 255, 255), RGB(128, 0, 0), RGB(0, 128, 0), RGB(0, 0, 128), RGB(192, 192, 192), RGB(0, 0, 0) // Black }; void DrawFractal(HDC hdc, const RECT& rect, FractalParams *p) { //double p = /*-1.25*/0.32, q = /*0.1*/0.043; //double p = 0.1, q = 0.43; //double p = p->zr; //double q = p->zi; double m = 100; double zoom_a = (p->xmax - p->xmin) / (rect.right - rect.left + 1); double zoom_b = (p->ymax - p->ymin) / (rect.bottom - rect.top + 1); int palette = p->expand * 16; COLORREF colorsExt[1024]; for (int c = 0; c < 16; ++c) { BYTE r = GetRValue(p->colors[c]); BYTE g = GetGValue(p->colors[c]); BYTE b = GetBValue(p->colors[c]); colorsExt[p->expand*c + p->expand / 2] = p->colors[c]; for (int i = 1; i < p->expand / 2 + p->expand % 2; ++i) colorsExt[p->expand*c + p->expand / 2 + i] = RGB(min(r + i * p->step, 255), min(g + i * p->step, 255), min(b + i * p->step, 255)); for (int i = 1; i < p->expand / 2 + 1; ++i) colorsExt[p->expand*c + p->expand / 2 - i] = RGB(max((int)r - i * p->step, 0), max((int)g - i * p->step, 0), max((int)b - i * p->step, 0)); } for (int j = rect.top; j <= rect.bottom; ++j) for (int i = rect.left; i <= rect.right; ++i) { double a = p->xmin + (i - rect.left)*zoom_a; double b = p->ymin + (j - rect.top)*zoom_b; double aN = 0, bN = 0; USHORT n = 0; while (true) { aN = a*a - b*b + p->zr/*p*/; bN = 2 * a*b + p->zi/*q*/; ++n; a = aN; b = bN; double s = a*a + b*b; if (m < s) { ::SetPixelV(hdc, i, j, colorsExt[n]); break; } if (m == s) { ::SetPixelV(hdc, i, j, colorsExt[0]); break; } if (n >= palette - 1) { ::SetPixelV(hdc, i, j, colorsExt[n]); break; } } } } bool SaveFractalAsDIB(GSCalcArg *arg, BYTE*& dib, size_t& dibLen, const RECT& rect, FractalParams *p, uint8_t& errorCode) { int width = rect.right - rect.left + 1; int height = rect.bottom - rect.top + 1; int rowBytes = 4 * width; dibLen = sizeof(BITMAPINFOHEADER) + height*(rowBytes + (rowBytes % 4 ? 4 - rowBytes % 4 : 0)) * sizeof(BYTE); if (!(dib = static_cast<BYTE*>(arg->image.memory_alloc(dibLen)))) { errorCode = ERROR1_OUT_OF_MEMORY; return false; } BYTE *bits = dib + sizeof(BITMAPINFOHEADER); BITMAPINFOHEADER *header = reinterpret_cast<BITMAPINFOHEADER*>(dib); BITMAPINFO *bmpInfo = reinterpret_cast<BITMAPINFO*>(dib); ::memset(header, 0, sizeof(BITMAPINFOHEADER)); header->biSize = sizeof(BITMAPINFOHEADER); header->biWidth = width; header->biHeight = -height; header->biCompression = BI_RGB; header->biPlanes = 1; header->biBitCount = 32; header->biSizeImage = static_cast<DWORD>(dibLen) - sizeof(BITMAPINFOHEADER); HDC screenDC = ::GetDC(NULL); HDC memoryDC = ::CreateCompatibleDC(screenDC); HBITMAP bmpNew = ::CreateCompatibleBitmap(screenDC, width, height); ::ReleaseDC(NULL, screenDC); if (!memoryDC || !bmpNew) { arg->image.memory_free(dib); dib = NULL; dibLen = 0; errorCode = ERROR1_OUT_OF_MEMORY; if (bmpNew) ::DeleteObject(bmpNew); if (memoryDC) ::DeleteDC(memoryDC); return false; } else { HBITMAP bmpOld = (HBITMAP)::SelectObject(memoryDC, bmpNew); DrawFractal(memoryDC, rect, p); ::SelectObject(memoryDC, bmpOld); ::GetDIBits(memoryDC, bmpNew, 0, height, bits, bmpInfo, DIB_RGB_COLORS); ::DeleteObject(bmpNew); ::DeleteDC(memoryDC); return true; } } extern "C" { // fractal(zr, zi, xmin, xmax, ymin, ymax, width, height, [colors], [expand], [step]) // // arguments: // zr, zi - transformation // xmin, xmax, ymin, ymax - zoom/offset // width, height - physical bitmap size // [colors] - 16-item RGB array/range with basic colors (optional) // [expand] - expands the number of colors 2-64 times (optional) // [step] - RGB step to obtain subsequent expanded colors out of the basic colors (optional) // returns: // a pointer to the allocated memory containing the fractal DIB bitmap; // to obtain a higher resulution image when printing use arg->image.dims.cx/.cy to specify the display rectangle // and width/height to specify the actual DIB bitmap dimensions __declspec(dllexport) double __cdecl fractal(GSCalcArg *arg) { uint8_t errorCode = 0; for (int i = 0; i < 8; ++i) if (arg->types[i] != DLL_ARGT_DOUBLE) { errorCode = ERROR1_INVALID_VALUE; break; } else if ((errorCode = arg->errors[i]) != 0) break; if (arg->types[11]/*...*/) errorCode = ERROR1_SYNTAX_ERROR; if (errorCode) { arg->errors[DLL_ARGC_RET] = errorCode; return arg->types[DLL_ARGC_RET] = DLL_ARGT_EMPTY; } struct FractalParams p = { 0 }; RECT r = { 0, 0, 0, 0 }; p.zr = arg->numbers[0]; // 1st DLL_ARGT_DOUBLE argument p.zi = arg->numbers[1]; p.xmin = arg->numbers[2]; p.xmax = arg->numbers[3]; p.ymin = arg->numbers[4]; p.ymax = arg->numbers[5]; r.right = static_cast<int>(arg->numbers[6]); r.bottom = static_cast<int>(arg->numbers[7]); if (!errorCode && !(errorCode = arg->errors[8])) { if (arg->types[8] == DLL_ARGT_ARRAY) { if (arg->array_dims[0].cy*arg->array_dims[0].cx != 16) // 0 - 1st array/range of the 11 arguments errorCode = ERROR1_INVALID_VALUE; ArrayItem x = { 0 }; int index = 0; for (x.row = 0; x.row < arg->array_dims[0].cy && index < 16 && !errorCode; ++x.row) { for (x.col = 0; x.col < arg->array_dims[0].cx && index < 16 && !errorCode; ++x.col) { arg->read_array(arg->env, 0, &x); if (x.type == DLL_ARGT_DOUBLE) p.colors[index++] = static_cast<long>(x.num); else if (x.type == DLL_ARGT_EMPTY) p.colors[index++] = ::defaultColors[index]; else errorCode = ERROR1_INVALID_VALUE; } } } else if (arg->types[8] == DLL_ARGT_EMPTY) ::memcpy(p.colors, ::defaultColors, 16 * sizeof(COLORREF)); else errorCode = ERROR1_INVALID_VALUE; } if (!errorCode && !(errorCode = arg->errors[9])) { if (arg->types[9] == DLL_ARGT_DOUBLE) { p.expand = static_cast<int>(arg->numbers[8]); // 8 - 9th number of the 11 arguments errorCode = arg->errors[8]; } else if (arg->types[9] == DLL_ARGT_EMPTY) p.expand = 64; else errorCode = ERROR1_INVALID_VALUE; } if (!errorCode && !(errorCode = arg->errors[10])) { if (arg->types[10] == DLL_ARGT_DOUBLE) { p.step = static_cast<int>(arg->numbers[9]); // 9 - 10th number of the 11 arguments errorCode = arg->errors[9]; } else if (arg->types[10] == DLL_ARGT_EMPTY) p.step = 4; else errorCode = ERROR1_INVALID_VALUE; } assert(p.expand > 0 && p.expand <= 64 && p.xmax > p.xmin && p.ymax > p.ymin && p.step >= 1 && p.step <= 127); if (p.expand < 1 || p.expand > 64 || p.xmax <= p.xmin || p.ymax <= p.ymin || p.step < 1 || p.step > 127 || r.right <= 0 || r.bottom <= 0) errorCode = ERROR1_INVALID_NUMBER; if (!errorCode) SaveFractalAsDIB(arg, arg->image.data, arg->image.size, r, &p, errorCode); arg->errors[DLL_ARGC_RET] = errorCode; return arg->types[DLL_ARGC_RET] = DLL_ARGT_PNG; } // image(path, cx, cy) // // arguments: // path - a bmp, jpg, png, gif file path // cx/cy - display image size (optional) // returns: // loads an image (bmp, jpg, png, gif) from a disk and displays it, optionally resing it // to the cx/cy dimensions (in screen pixels) __declspec(dllexport) double __cdecl image(GSCalcArg *arg) { uint8_t errorCode = 0; if (arg->types[0] != DLL_ARGT_TEXT || arg->types[1] != DLL_ARGT_DOUBLE && arg->types[1] != DLL_ARGT_EMPTY || arg->types[2] != DLL_ARGT_DOUBLE && arg->types[2] != DLL_ARGT_EMPTY) errorCode = ERROR1_INVALID_VALUE; if (arg->types[3]/*...*/) errorCode = ERROR1_SYNTAX_ERROR; if (!errorCode && arg->errors[0]) errorCode = arg->errors[0]; ::strcpy_s(arg->strings[DLL_ARGC_RET], MAXINPUT, arg->strings[0]); arg->image.dims.cx = static_cast<uint32_t>(arg->numbers[0]); arg->image.dims.cy = static_cast<uint32_t>(arg->numbers[1]); // arg->image.data must remain NULL arg->errors[DLL_ARGC_RET] = errorCode; return arg->types[DLL_ARGC_RET] = DLL_ARGT_PNG; } // filter(range, column, number) // // arguments: // range - a range/array // column - column index (from 0 to the number of columns in 'range' - 1) // number - numeric value to perform equality check // returns: // rows of 'range' containing 'value' in the specified column __declspec(dllexport) double __cdecl filter(GSCalcArg *arg) { uint8_t errorCode = 0; if (arg->types[0] != DLL_ARGT_ARRAY || arg->types[1] != DLL_ARGT_DOUBLE || arg->types[2] != DLL_ARGT_DOUBLE) errorCode = ERROR1_INVALID_VALUE; if (arg->types[3]/*...*/) errorCode = ERROR1_SYNTAX_ERROR; if (!errorCode && arg->errors[0]) errorCode = arg->errors[0]; if (!errorCode && arg->errors[1]) errorCode = arg->errors[1]; if (!errorCode && arg->errors[2]) errorCode = arg->errors[2]; ArrayItem x = { 0 }; int outRow = 0; for (uint32_t inRow = 0; inRow < arg->array_dims[0].cy && !errorCode; ++inRow) { x.row = inRow; x.col = static_cast<int>(arg->numbers[0]); if (arg->read_array(arg->env, 0, &x) == -1) { errorCode = static_cast<BYTE>(x.err_code); break; } bool match = false; if (x.type == DLL_ARGT_DOUBLE) match = (x.num == arg->numbers[1] || x.err_code && x.err_code == arg->errors[2]); else if (x.type == DLL_ARGT_EMPTY) match = (arg->numbers[1] == 0); else errorCode = ERROR1_INVALID_VALUE; if (match) { for (x.col = 0; x.col < arg->array_dims[0].cx && !errorCode; ++x.col) { x.row = inRow; arg->read_array(arg->env, 0, &x); x.row = outRow; if (arg->write_array(arg->env, DLL_ARGC_RET, &x) == -1) errorCode = static_cast<BYTE>(x.err_code); } ++outRow; } } arg->errors[DLL_ARGC_RET] = (!outRow ? ERROR1_NULL_VALUE : 0); return arg->types[DLL_ARGC_RET] = DLL_ARGT_ARRAY; return 0; } // messageIf(condition, text) // // arguments: // condition, message text // returns: // if condition != 0, 'messageIf' displays 'text' as a message box after each update/recalculation __declspec(dllexport) double __cdecl messageIf(GSCalcArg *arg) { uint8_t errorCode = 0; if (arg->types[0] != DLL_ARGT_DOUBLE && arg->types[0] != DLL_ARGT_EMPTY || arg->types[1] != DLL_ARGT_TEXT) errorCode = ERROR1_INVALID_VALUE; if (arg->types[2]/*...*/) errorCode = ERROR1_SYNTAX_ERROR; if (!errorCode && arg->errors[0]) errorCode = arg->errors[0]; if (!errorCode && arg->errors[1]) errorCode = arg->errors[1]; if (!errorCode) { ::strncpy(arg->strings[DLL_ARGC_RET], arg->strings[0], 1024); arg->strings[DLL_ARGC_RET][1024] = 0; // actually it's guaranteed on the entry return arg->types[DLL_ARGC_RET] = (arg->numbers[0] ? DLL_ARGT_MESSAGE : DLL_ARGT_TEXT); } else { arg->errors[DLL_ARGC_RET] = errorCode; return arg->types[DLL_ARGC_RET] = DLL_ARGT_EMPTY; } return 0; } }