diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/choice_enum.h | 2 | ||||
-rw-r--r-- | wx+/graph.cpp | 22 | ||||
-rw-r--r-- | wx+/graph.h | 4 | ||||
-rw-r--r-- | wx+/grid.cpp | 17 | ||||
-rw-r--r-- | wx+/grid.h | 4 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 36 | ||||
-rw-r--r-- | wx+/image_tools.cpp | 58 | ||||
-rw-r--r-- | wx+/image_tools.h | 3 |
8 files changed, 93 insertions, 53 deletions
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index 626aa39a..a590861a 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -41,7 +41,7 @@ struct EnumDescrList { EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = {}) { - descrList.push_back({ value, { text, tooltip } }); + descrList.push_back({value, {text, tooltip}}); return *this; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index ba87299e..be75ac4d 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -36,7 +36,7 @@ double zen::nextNiceNumber(double blockSize) //round to next number which is a c assert(1 <= a && a < 10); //have a look at leading two digits: "nice" numbers start with 1, 2, 2.5 and 5 - const double steps[] = { 1, 2, 2.5, 5, 10 }; + const double steps[] = {1, 2, 2.5, 5, 10}; return e * numeric::nearMatch(a, std::begin(steps), std::end(steps)); } @@ -48,16 +48,16 @@ wxColor getDefaultColor(size_t pos) switch (pos % 10) { //*INDENT-OFF* - case 0: return { 0, 69, 134 }; //blue - case 1: return { 255, 66, 14 }; //red - case 2: return { 255, 211, 32 }; //yellow - case 3: return { 87, 157, 28 }; //green - case 4: return { 126, 0, 33 }; //royal - case 5: return { 131, 202, 255 }; //light blue - case 6: return { 49, 64, 4 }; //dark green - case 7: return { 174, 207, 0 }; //light green - case 8: return { 75, 31, 111 }; //purple - case 9: return { 255, 149, 14 }; //orange + case 0: return { 0, 69, 134}; //blue + case 1: return {255, 66, 14}; //red + case 2: return {255, 211, 32}; //yellow + case 3: return { 87, 157, 28}; //green + case 4: return {126, 0, 33}; //royal + case 5: return {131, 202, 255}; //light blue + case 6: return { 49, 64, 4}; //dark green + case 7: return {174, 207, 0}; //light green + case 8: return { 75, 31, 111}; //purple + case 9: return {255, 149, 14}; //orange //*INDENT-ON* } assert(false); diff --git a/wx+/graph.h b/wx+/graph.h index c843b09b..288ef9e7 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -218,7 +218,7 @@ public: void addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes()); void clearCurves() { curves_.clear(); } - static wxColor getBorderColor() { return { 130, 135, 144 }; } //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal... + static wxColor getBorderColor() { return {130, 135, 144}; } //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal... class MainAttributes { @@ -330,7 +330,7 @@ private: CurveList curves_; //perf!!! generating the font is *very* expensive! => buffer for Graph2D::render()! - const wxFont labelFont_ { wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial" }; + const wxFont labelFont_{wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial"}; }; } diff --git a/wx+/grid.cpp b/wx+/grid.cpp index cd91b1af..0111ccf7 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -27,8 +27,8 @@ using namespace zen; //let's NOT create wxWidgets objects statically: -wxColor GridData::getColorSelectionGradientFrom() { return { 137, 172, 255 }; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1 -wxColor GridData::getColorSelectionGradientTo () { return { 225, 234, 255 }; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1 +wxColor GridData::getColorSelectionGradientFrom() { return {137, 172, 255}; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1 +wxColor GridData::getColorSelectionGradientTo () { return {225, 234, 255}; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1 int GridData::getColumnGapLeft() { return fastFromDIP(4); } @@ -493,8 +493,8 @@ public: const int yFrom = refParent().CalcUnscrolledPosition(clientRect.GetTopLeft ()).y; const int yTo = refParent().CalcUnscrolledPosition(clientRect.GetBottomRight()).y; - return { std::max(yFrom / rowHeight_, 0), - std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount()) }; + return {std::max(yFrom / rowHeight_, 0), + std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount())}; } private: @@ -1351,6 +1351,7 @@ private: return; const double mouseDragSpeedIncScrollU = MOUSE_DRAG_ACCELERATION_DIP * wnd_.rowLabelWin_.getRowHeight() / pixelsPerUnitY; //unit: [scroll units / (DIP * sec)] + //design alternative: "Dynamic autoscroll based on escape velocity": https://devblogs.microsoft.com/oldnewthing/20210128-00/?p=104768 auto autoScroll = [&](int overlapPix, double& toScroll) { @@ -1986,7 +1987,7 @@ void Grid::setColumnConfig(const std::vector<Grid::ColAttributes>& attr) assert(ca.type != ColumnType::none); if (ca.visible) - visCols.push_back({ ca.type, ca.offset, std::max(ca.stretch, 0) }); + visCols.push_back({ca.type, ca.offset, std::max(ca.stretch, 0)}); } //"ownership" of visible columns is now within Grid @@ -2101,10 +2102,10 @@ Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const { accWidth += cw.width; if (posX < accWidth) - return { cw.type, posX + cw.width - accWidth, cw.width }; + return {cw.type, posX + cw.width - accWidth, cw.width}; } } - return { ColumnType::none, 0, 0 }; + return {ColumnType::none, 0, 0}; } @@ -2386,7 +2387,7 @@ std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //eval else width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH_DIP if set via configuration - output.push_back({ vc.type, width }); + output.push_back({vc.type, width}); } return output; } @@ -387,7 +387,7 @@ std::vector<Grid::ColAttributes> convertColAttributes(const std::vector<ColAttrR { std::vector<Grid::ColAttributes> output; for (const ColAttrReal& ca : makeConsistent(attribs, defaults)) - output.push_back({ static_cast<ColumnType>(ca.type), ca.offset, ca.stretch, ca.visible }); + output.push_back({static_cast<ColumnType>(ca.type), ca.offset, ca.stretch, ca.visible}); return output; } @@ -399,7 +399,7 @@ std::vector<ColAttrReal> convertColAttributes(const std::vector<Grid::ColAttribu std::vector<ColAttrReal> output; for (const Grid::ColAttributes& ca : attribs) - output.push_back({ static_cast<ColTypeReal>(ca.type), ca.offset, ca.stretch, ca.visible }); + output.push_back({static_cast<ColTypeReal>(ca.type), ca.offset, ca.stretch, ca.visible}); return output; } } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 42114ebb..fbfeb0d8 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -50,27 +50,23 @@ ImageHolder xbrzScale(int width, int height, const unsigned char* imageRgb, cons *out++ = xbrz::makePixel(*alpha++, rgb[0], rgb[1], rgb[2]); } //----------------------------------------------------- - xbrz::scale(hqScale, //size_t factor, //valid range: 2 - SCALE_FACTOR_MAX - argbSrc, //const uint32_t* src, - xbrTrg, //uint32_t* trg, - width, height, //int srcWidth, int srcHeight, - xbrz::ColorFormat::ARGB_UNBUFFERED); //ColorFormat colFmt, - //test: total xBRZ scaling time with ARGB: 300ms, ARGB_UNBUFFERED: 50ms + xbrz::scale(hqScale, //size_t factor - valid range: 2 - SCALE_FACTOR_MAX + argbSrc, //const uint32_t* src + xbrTrg, //uint32_t* trg + width, height, //int srcWidth, int srcHeight + xbrz::ColorFormat::argbUnbuffered); //ColorFormat colFmt + //test: total xBRZ scaling time with ARGB: 300ms, ARGB unbuffered: 50ms //----------------------------------------------------- //convert BGRA to RGB + alpha ImageHolder trgImg(hqWidth, hqHeight, true /*withAlpha*/); - { - unsigned char* rgb = trgImg.getRgb(); - unsigned char* alpha = trgImg.getAlpha(); - std::for_each(xbrTrg, xbrTrg + hqWidth * hqHeight, [&](uint32_t col) - { - *alpha++ = xbrz::getAlpha(col); - *rgb++ = xbrz::getRed (col); - *rgb++ = xbrz::getGreen(col); - *rgb++ = xbrz::getBlue (col); - }); - } + std::for_each(xbrTrg, xbrTrg + hqWidth * hqHeight, [rgb = trgImg.getRgb(), alpha = trgImg.getAlpha()](uint32_t col) mutable + { + *alpha++ = xbrz::getAlpha(col); + *rgb++ = xbrz::getRed (col); + *rgb++ = xbrz::getGreen(col); + *rgb++ = xbrz::getBlue (col); + }); return trgImg; } @@ -128,7 +124,7 @@ private: Protected<std::vector<std::pair<std::string, ImageHolder>>> result_; using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>; - std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), Zstr("xBRZ Scaler")) }; + std::optional<ThreadGroup<TaskType>> threadGroup_{ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), Zstr("xBRZ Scaler"))}; //hardware_concurrency() == 0 if "not computable or well defined" }; @@ -296,8 +292,8 @@ const wxImage& ImageBuffer::getImage(const std::string& name, int maxWidth /*opt if (rawImg.GetHeight() >= outHeight) //=> skip needless xBRZ upscaling it = imagesOut_.emplace(imkey, shrinkImage(rawImg, -1 /*maxWidth*/, outHeight)).first; else if (rawImg.GetHeight() >= 0.9 * outHeight) //almost there: also no need for xBRZ-scale - //however: for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"! - it = imagesOut_.emplace(imkey, rawImg.Scale(numeric::intDivRound(outHeight * rawImg.GetWidth(), rawImg.GetHeight()), outHeight, wxIMAGE_QUALITY_BILINEAR)).first; + it = imagesOut_.emplace(imkey, bilinearScale(rawImg, numeric::intDivRound(outHeight * rawImg.GetWidth(), rawImg.GetHeight()), outHeight)).first; + //however: for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale" else it = imagesOut_.emplace(imkey, shrinkImage(getScaledImage(name), -1 /*maxWidth*/, outHeight)).first; } diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 6ba95c5e..bbc8cee7 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -8,6 +8,7 @@ #include <zen/string_tools.h> #include <zen/zstring.h> #include <wx/app.h> +#include <xBRZ/src/xbrz_tools.h> using namespace zen; @@ -91,12 +92,12 @@ void copyImageLayover(const wxImage& src, for (int x = 0; x < srcWidth; ++x) { const int w1 = *srcAlpha; //alpha-composition interpreted as weighted average - const int w2 = *trgAlpha * (255 - w1) / 255; + const int w2 = numeric::intDivRound(*trgAlpha * (255 - w1), 255); const int wSum = w1 + w2; auto calcColor = [w1, w2, wSum](unsigned char colsrc, unsigned char colTrg) { - return static_cast<unsigned char>(wSum == 0 ? 0 : (colsrc * w1 + colTrg * w2) / wSum); + return static_cast<unsigned char>(wSum == 0 ? 0 : numeric::intDivRound(colsrc * w1 + colTrg * w2, wSum)); }; trgRgb[0] = calcColor(srcRgb[0], trgRgb[0]); trgRgb[1] = calcColor(srcRgb[1], trgRgb[1]); @@ -138,7 +139,7 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay const int img2Height = img2.GetHeight(); const wxSize newSize = dir == ImageStackLayout::horizontal ? - wxSize(img1Width + gap + img2Width, std::max(img1Height, img2Height)) : + wxSize(img1Width + gap + img2Width, std::max(img1Height, img2Height)) : wxSize(std::max(img1Width, img2Width), img1Height + gap + img2Height); wxImage output(newSize); @@ -181,7 +182,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const //assert(!contains(text, L"&")); //accelerator keys not supported here wxString textFmt = replaceCpy(text, L"&", L"", false); - const std::vector<std::pair<wxString, wxSize>> lineInfo = getTextExtentInfo(textFmt, font); + const std::vector<std::pair<wxString, wxSize>> lineInfo = getTextExtentInfo(textFmt, font); int maxWidth = 0; int lineHeight = 0; @@ -197,8 +198,8 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const { wxMemoryDC dc(newBitmap); - if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) - dc.SetLayoutDirection(wxLayout_RightToLeft); //handle e.g. "weak" bidi characters: -> arrows in hebrew/arabic + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + dc.SetLayoutDirection(wxLayout_RightToLeft); //handle e.g. "weak" bidi characters: -> arrows in hebrew/arabic dc.SetBackground(*wxWHITE_BRUSH); dc.Clear(); @@ -238,7 +239,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const for (int i = 0; i < pixelCount; ++i) { //black(0,0,0) becomes wxIMAGE_ALPHA_OPAQUE(255), while white(255,255,255) becomes wxIMAGE_ALPHA_TRANSPARENT(0) - *alpha++ = static_cast<unsigned char>((255 - rgb[0] + 255 - rgb[1] + 255 - rgb[2]) / 3); //mixed-mode arithmetics! + *alpha++ = static_cast<unsigned char>(numeric::intDivRound(3 * 255 - rgb[0] - rgb[1] - rgb[2], 3)); //mixed-mode arithmetics! *rgb++ = col.Red (); // *rgb++ = col.Green(); //apply actual text color @@ -312,6 +313,46 @@ wxImage zen::resizeCanvas(const wxImage& img, wxSize newSize, int alignment) } +wxImage zen::bilinearScale(const wxImage& img, int width, int height) +{ + assert(img.HasAlpha()); + const auto imgReader = [rgb = img.GetData(), alpha = img.GetAlpha(), srcWidth = img.GetSize().x](int x, int y, xbrz::BytePixel& pix) + { + const int idx = y * srcWidth + x; + const unsigned char* const ptr = rgb + idx * 3; + + const unsigned char a = alpha[idx]; + pix[0] = a; + pix[1] = xbrz::premultiply(ptr[0], a); //r + pix[2] = xbrz::premultiply(ptr[1], a); //g + pix[3] = xbrz::premultiply(ptr[2], a); //b + }; + + wxImage imgOut(width, height); + imgOut.SetAlpha(); + + const auto imgWriter = [rgb = imgOut.GetData(), alpha = imgOut.GetAlpha()](const xbrz::BytePixel& pix) mutable + { + const unsigned char a = pix[0]; + * alpha++ = a; + * rgb++ = xbrz::demultiply(pix[1], a); //r + *rgb++ = xbrz::demultiply(pix[2], a); //g + *rgb++ = xbrz::demultiply(pix[3], a); //b + }; + + xbrz::bilinearScaleSimple(imgReader, //PixReader srcReader + img.GetSize().x, //int srcWidth + img.GetSize().y, //int srcHeight + imgWriter, //PixWriter trgWriter + width, //int trgWidth + height, //int trgHeight + 0, //int yFirst + height); //int yLast + return imgOut; + //return img.Scale(width, height, wxIMAGE_QUALITY_BILINEAR); +} + + wxImage zen::shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/) { wxSize newSize = img.GetSize(); @@ -330,8 +371,7 @@ wxImage zen::shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxH if (newSize == img.GetSize()) return img; - return img.Scale(newSize.x, newSize.y, wxIMAGE_QUALITY_BILINEAR); //looks sharper than wxIMAGE_QUALITY_HIGH! - //perf: use xbrz::bilinearScale instead? only about 10% shorter runtime + return bilinearScale(img, newSize.x, newSize.y); //looks sharper than wxIMAGE_QUALITY_HIGH! } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index 0f0fb9c2..c2fed4c1 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -50,6 +50,9 @@ void convertToVanillaImage(wxImage& img); //add alpha channel if missing + remov //wxColor hsvColor(double h, double s, double v); //h within [0, 360), s, v within [0, 1] +//does *not* fuck up alpha channel like naive bilinear implementations, e.g. wxImage::Scale() +wxImage bilinearScale(const wxImage& img, int width, int height); + wxImage shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/); inline wxImage shrinkImage(const wxImage& img, int maxSize) { return shrinkImage(img, maxSize, maxSize); } |