diff --git a/yg/geometry_batcher.cpp b/yg/geometry_batcher.cpp index 4f264d7f10..994946276d 100644 --- a/yg/geometry_batcher.cpp +++ b/yg/geometry_batcher.cpp @@ -746,5 +746,11 @@ namespace yg base_t::setClipRect(rect); } + int GeometryBatcher::aaShift() const + { + return m_aaShift; + } + + } // namespace gl } // namespace yg diff --git a/yg/geometry_batcher.hpp b/yg/geometry_batcher.hpp index 489528a134..e1af72c936 100644 --- a/yg/geometry_batcher.hpp +++ b/yg/geometry_batcher.hpp @@ -162,6 +162,14 @@ namespace yg void setRenderTarget(shared_ptr const & rt); + void addTexturedVertices(m2::PointF const * coords, + m2::PointF const * texCoords, + unsigned size, + double depth, + int pageID); + + int aaShift() const; + private: void drawGlyph(m2::PointD const & ptOrg, @@ -181,12 +189,6 @@ namespace yg float x0, float y0, float x1, float y1, double depth, int pageID); - - void addTexturedVertices(m2::PointF const * coords, - m2::PointF const * texCoords, - unsigned size, - double depth, - int pageID); }; } } diff --git a/yg/path_renderer.cpp b/yg/path_renderer.cpp new file mode 100644 index 0000000000..bd35d1561a --- /dev/null +++ b/yg/path_renderer.cpp @@ -0,0 +1,95 @@ +#include "../base/SRC_FIRST.hpp" + +#include "path_renderer.hpp" +#include "resource_style.hpp" +#include "skin.hpp" + +namespace yg +{ + namespace gl + { + + PathRenderer::PathRenderer(base_t::Params const & params) : base_t(params) + {} + + void PathRenderer::drawPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth) + { + ASSERT_GREATER_OR_EQUAL(pointsCount, 2, ()); + ResourceStyle const * style(skin()->fromID(styleID)); + + ASSERT(style->m_cat == ResourceStyle::ELineStyle, ()); + LineStyle const * lineStyle = static_cast(style); + + if (lineStyle->m_isSolid) + { + for (size_t i = 0; i < pointsCount - 1; ++i) + { + m2::PointD dir = points[i + 1] - points[i]; + dir *= 1.0 / dir.Length(m2::PointD(0, 0)); + m2::PointD norm(-dir.y, dir.x); + + float segLen = points[i + 1].Length(points[i]); + float geomHalfWidth = (lineStyle->m_penInfo.m_w + 4 - aaShift() * 2) / 2.0; + + float texMinX = lineStyle->m_texRect.minX() + 1; + float texMaxX = lineStyle->m_texRect.maxX() - 1; + + float texMinY = lineStyle->m_texRect.maxY() - aaShift(); + float texMaxY = lineStyle->m_texRect.minY() + aaShift(); + + float texCenterX = (texMinX + texMaxX) / 2; + + m2::PointF const fNorm = norm * geomHalfWidth; // enough to calc it once + m2::PointF const fDir(fNorm.y, -fNorm.x); + + m2::PointF coords[12] = + { + /// left round cap + m2::PointF(points[i].x - fDir.x + fNorm.x, points[i].y - fDir.y + fNorm.y), + m2::PointF(points[i].x - fDir.x - fNorm.x, points[i].y - fDir.y - fNorm.y), + m2::PointF(points[i].x - fNorm.x, points[i].y - fNorm.y), + m2::PointF(points[i].x + fNorm.x, points[i].y + fNorm.y), + /// inner part + m2::PointF(points[i].x + fNorm.x, points[i].y + fNorm.y), + m2::PointF(points[i].x - fNorm.x, points[i].y - fNorm.y), + m2::PointF(points[i + 1].x - fNorm.x, points[i + 1].y - fNorm.y), + m2::PointF(points[i + 1].x + fNorm.x, points[i + 1].y + fNorm.y), + /// right round cap + m2::PointF(points[i + 1].x + fNorm.x, points[i + 1].y + fNorm.y), + m2::PointF(points[i + 1].x - fNorm.x, points[i + 1].y - fNorm.y), + m2::PointF(points[i + 1].x + fDir.x - fNorm.x, points[i + 1].y + fDir.y - fNorm.y), + m2::PointF(points[i + 1].x + fDir.x + fNorm.x, points[i + 1].y + fDir.y + fNorm.y) + }; + + shared_ptr texture = skin()->pages()[lineStyle->m_pageID]->texture(); + + m2::PointF texCoords[12] = + { + /// left round cap + texture->mapPixel(m2::PointF(texMinX, texMinY)), + texture->mapPixel(m2::PointF(texMinX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + /// inner part + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + /// right round cap + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texMaxX, texMaxY)), + texture->mapPixel(m2::PointF(texMaxX, texMinY)) + }; + + addTexturedVertices(coords, texCoords, 4, depth, lineStyle->m_pageID); + addTexturedVertices(coords + 4, texCoords + 4, 4, depth, lineStyle->m_pageID); + addTexturedVertices(coords + 8, texCoords + 8, 4, depth, lineStyle->m_pageID); + } + } + else + base_t::drawPath(points, pointsCount, styleID, depth); + } + } +} + diff --git a/yg/path_renderer.hpp b/yg/path_renderer.hpp new file mode 100644 index 0000000000..da9bed1ec1 --- /dev/null +++ b/yg/path_renderer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "geometry_batcher.hpp" +#include "../geometry/point2d.hpp" + +namespace yg +{ + namespace gl + { + class PathRenderer : public GeometryBatcher + { + private: + public: + + typedef GeometryBatcher base_t; + + PathRenderer(base_t::Params const & params); + + void drawPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth); + }; + } +} diff --git a/yg/pen_info.cpp b/yg/pen_info.cpp index 974954f2ab..0157170aa6 100644 --- a/yg/pen_info.cpp +++ b/yg/pen_info.cpp @@ -10,15 +10,16 @@ namespace yg PenInfo::PenInfo(Color const & color, double w, double const * pattern, size_t patternSize, double offset) : m_color(color), m_w(w), m_offset(offset), m_isSolid(false) { - if (m_w < 1.5) - m_w = 1.5; + if (m_w < 1.25) + m_w = 1.25; /// if pattern is solid if ((pattern == 0 ) || (patternSize == 0)) { + m_isSolid = true; // m_pat.push_back(5); - m_pat.push_back(50); - m_pat.push_back(0); +// m_pat.push_back(50); +// m_pat.push_back(0); } else { diff --git a/yg/skin_page.cpp b/yg/skin_page.cpp index 6b7270e6ec..a77149ffa5 100644 --- a/yg/skin_page.cpp +++ b/yg/skin_page.cpp @@ -1,5 +1,16 @@ #include "../base/SRC_FIRST.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "data_formats.hpp" #include "skin_page.hpp" #include "texture.hpp" @@ -12,6 +23,23 @@ #include "../std/bind.hpp" #include "../std/numeric.hpp" +template +struct AggTraits +{ +}; + +template <> +struct AggTraits +{ + typedef agg::pixfmt_rgba32 pixfmt_t; +}; + +template <> +struct AggTraits +{ + typedef agg::pixfmt_rgb4444 pixfmt_t; +}; + namespace yg { typedef gl::Texture TDynamicTexture; @@ -252,14 +280,18 @@ namespace yg if (penInfo.m_isSolid) { - uint32_t colorHandle = mapColor(penInfo.m_color); - m2::RectU texRect = m_packer.find(colorHandle).second; - m_styles[colorHandle] = boost::shared_ptr( + m2::Packer::handle_t handle = m_packer.pack( + ceil(penInfo.m_w + 4), + ceil(penInfo.m_w + 4)); + m2::RectU texRect = m_packer.find(handle).second; + m_penUploadCommands.push_back(PenUploadCmd(penInfo, texRect)); + m_penInfoMap[penInfo] = handle; + + m_styles[handle] = boost::shared_ptr( new LineStyle(false, texRect, m_pageID, penInfo)); - m_penInfoMap[penInfo] = colorHandle; } else { @@ -324,56 +356,120 @@ namespace yg TDynamicTexture::pixel_t penColor = penColorTranslucent; gil::get_color(penColor, gil::alpha_t()) = penInfoColor.a; - /// First two and last two rows of a pattern are filled - /// with a penColorTranslucent pixel for the antialiasing. - for (size_t y = 0; y < 2; ++y) - for (size_t x = 0; x < rect.SizeX(); ++x) - v(x, y) = penColorTranslucent; - - for (size_t y = rect.SizeY() - 2; y < rect.SizeY(); ++y) - for (size_t x = 0; x < rect.SizeX(); ++x) - v(x, y) = penColorTranslucent; - - for (size_t y = 2; y < rect.SizeY() - 2; ++y) + if (penInfo.m_isSolid) { - v(0, y) = penColor; - v(1, y) = penColor; - v(rect.SizeX() - 2, y) = penColor; - v(rect.SizeX() - 1, y) = penColor; - } - for (size_t y = 2; y < rect.SizeY() - 2; ++y) - { - double curLen = 0; + /// draw circle + agg::rendering_buffer buf( + (unsigned char *)&v(0, 0), + rect.SizeX(), + rect.SizeY(), + rect.SizeX() * sizeof(TDynamicTexture::pixel_t) + ); - TDynamicTexture::pixel_t px = penColor; + typedef AggTraits::pixfmt_t agg_pixfmt_t; - /// In general case this code is incorrect. - /// TODO : Make a pattern start and end with a dash. - v(curLen, y) = px; - v(curLen + 1, y) = px; + agg_pixfmt_t pixfmt(buf); + agg::renderer_base rbase(pixfmt); + rbase.clear(agg::rgba8(penInfo.m_color.r, + penInfo.m_color.g, + penInfo.m_color.b, + 0)); - for (size_t i = 0; i < penInfo.m_pat.size(); ++i) + agg::scanline_u8 s; + agg::rasterizer_scanline_aa<> rasterizer; + if (penInfo.m_w > 2) { - for (size_t j = 0; j < penInfo.m_pat[i]; ++j) + agg::ellipse ell; + float r = penInfo.m_w / 2.0; + ell.init(r + 2, r + 2, r, r, 100); + rasterizer.add_path(ell); + + agg::render_scanlines_aa_solid(rasterizer, + s, + rbase, + agg::rgba8(penInfo.m_color.r, + penInfo.m_color.g, + penInfo.m_color.b, + penInfo.m_color.a)); + + /// making alpha channel opaque + for (size_t x = 2; x < v.width() - 2; ++x) + for (size_t y = 2; y < v.height() - 2; ++y) + { + unsigned char alpha = gil::get_color(v(x, y), gil::alpha_t()); + if (alpha != 0) + { + v(x, y) = penColor; + gil::get_color(v(x, y), gil::alpha_t()) = alpha; + } + } + } + else + { + gil::fill_pixels( + gil::subimage_view(v, 2, 2, rect.SizeX() - 4, rect.SizeY() - 4), + penColor + ); + } + } + else + { + /// First two and last two rows of a pattern are filled + /// with a penColorTranslucent pixel for the antialiasing. + for (size_t y = 0; y < 2; ++y) + for (size_t x = 0; x < rect.SizeX(); ++x) + v(x, y) = penColorTranslucent; + + for (size_t y = rect.SizeY() - 2; y < rect.SizeY(); ++y) + for (size_t x = 0; x < rect.SizeX(); ++x) + v(x, y) = penColorTranslucent; + + /// first and last two pixels filled with penColorTranslucent + for (size_t y = 2; y < rect.SizeY() - 2; ++y) + { + v(0, y) = penColorTranslucent; + v(1, y) = penColorTranslucent; + v(rect.SizeX() - 2, y) = penColorTranslucent; + v(rect.SizeX() - 1, y) = penColorTranslucent; + } + + /// draw pattern + for (size_t y = 2; y < rect.SizeY() - 2; ++y) + { + double curLen = 0; + + TDynamicTexture::pixel_t px = penColor; + + /// In general case this code is incorrect. + /// TODO : Make a pattern start and end with a dash. + v(curLen, y) = px; + v(curLen + 1, y) = px; + + for (size_t i = 0; i < penInfo.m_pat.size(); ++i) { - uint32_t val = (i + 1) % 2; + for (size_t j = 0; j < penInfo.m_pat[i]; ++j) + { + uint32_t val = (i + 1) % 2; - if (val == 0) - gil::get_color(px, gil::alpha_t()) = 0; - else - gil::get_color(px, gil::alpha_t()) = penInfoColor.a; + if (val == 0) + gil::get_color(px, gil::alpha_t()) = 0; + else + gil::get_color(px, gil::alpha_t()) = penInfoColor.a; - v(curLen + j + 2, y) = px; + v(curLen + j + 2, y) = px; + } + + v(curLen + 2 + penInfo.m_pat[i], y) = px; + v(curLen + 2 + penInfo.m_pat[i] + 1, y) = px; + + curLen += penInfo.m_pat[i]; } - - v(curLen + 2 + penInfo.m_pat[i], y) = px; - v(curLen + 2 + penInfo.m_pat[i] + 1, y) = px; - - curLen += penInfo.m_pat[i]; } } dynTexture->upload(&v(0, 0), rect); + + lodepng_write_view("test.png", v); } m_penUploadCommands.clear(); } diff --git a/yg/text_renderer.hpp b/yg/text_renderer.hpp index cd26436058..edadd32e71 100644 --- a/yg/text_renderer.hpp +++ b/yg/text_renderer.hpp @@ -1,6 +1,6 @@ #pragma once -#include "geometry_batcher.hpp" +#include "path_renderer.hpp" #include "../geometry/tree4d.hpp" @@ -13,7 +13,7 @@ namespace yg { class BaseTexture; - class TextRenderer : public GeometryBatcher + class TextRenderer : public PathRenderer { class TextObj { @@ -47,7 +47,7 @@ namespace yg public: - typedef GeometryBatcher base_t; + typedef PathRenderer base_t; struct Params : base_t::Params { bool m_useTextLayer; diff --git a/yg/texture.hpp b/yg/texture.hpp index 3faa0c7ebb..c8a30ad184 100644 --- a/yg/texture.hpp +++ b/yg/texture.hpp @@ -111,6 +111,7 @@ namespace yg { public: + typedef Traits traits_t; typedef typename Traits::pixel_t pixel_t; typedef typename Traits::const_pixel_t const_pixel_t; typedef typename Traits::view_t view_t; diff --git a/yg/yg.pro b/yg/yg.pro index 7ab9957f18..2dc6e55164 100644 --- a/yg/yg.pro +++ b/yg/yg.pro @@ -8,7 +8,7 @@ DEFINES += YG_LIBRARY ROOT_DIR = .. DEPENDENCIES = geometry coding base freetype fribidi expat -INCLUDEPATH += $$ROOT_DIR/3party/freetype/include +INCLUDEPATH += $$ROOT_DIR/3party/freetype/include $$ROOT_DIR/3party/agg include($$ROOT_DIR/common.pri) @@ -56,7 +56,8 @@ SOURCES += \ ft2_debug.cpp \ geometry_batcher.cpp \ text_renderer.cpp \ - layer_manager.cpp + layer_manager.cpp \ + path_renderer.cpp HEADERS += \ internal/opengl.hpp \ @@ -100,7 +101,8 @@ HEADERS += \ screen.hpp \ layer_manager.hpp \ gpu_state.hpp \ - defines.hpp + defines.hpp \ + path_renderer.hpp !iphonesimulator-g++42 { !iphonedevice-g++42 { diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index 52eaffabf3..e5d0494b05 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -99,6 +99,17 @@ namespace p->drawPath(&m_pathes[i][0], m_pathes[i].size(), p->skin()->mapPenInfo(m_axisPenInfo), m_depthes[i]); } } + + void makeStar(vector & pts, m2::RectD const & r) + { + pts.push_back(m2::PointD(r.minX(), r.maxY())); + pts.push_back(m2::PointD(r.Center().x, r.minY())); + pts.push_back(m2::PointD(r.maxX(), r.maxY())); + pts.push_back(m2::PointD(r.minX(), r.minY() + r.SizeY() / 3)); + pts.push_back(m2::PointD(r.maxX(), r.minY() + r.SizeY() / 3)); + pts.push_back(m2::PointD(r.minX(), r.maxY())); + } + }; struct TestDrawPathWithSkinPageMiss : public TestDrawPathBase @@ -326,8 +337,31 @@ namespace } }; + struct TestDrawPathSolidDiffWidth : TestDrawPathBase + { + typedef TestDrawPathBase base_t; - struct TestDrawPathSolid : TestDrawPathBase + void Init() + { + base_t::Init(); + + vector testPoints; + + int starCount = 10; + m2::PointD starSize(50, 50); + m2::PointD pt(10, 10); + + for (int i = 0; i < starCount; ++i) + { + base_t::makeStar(testPoints, m2::RectD(pt, pt + starSize)); + AddTest(testPoints, vector(), yg::Color(255, 0, 0, 255), i + 1); + pt = pt + m2::PointD(starSize.x + i + 3, 0); + testPoints.clear(); + } + } + }; + + struct TestDrawPathSolid1PX : TestDrawPathBase { typedef TestDrawPathBase base_t; @@ -343,6 +377,73 @@ namespace testPoints.push_back(m2::PointD(220, 269)); testPoints.push_back(m2::PointD(320, 270)); + AddTest(testPoints, testPattern, yg::Color(255, 0, 0, 255), 1); + + testPattern.push_back(20); + testPattern.push_back(20); + testPattern.push_back(20); + testPattern.push_back(20); + + testPoints.clear(); + testPoints.push_back(m2::PointD(420, 300)); + testPoints.push_back(m2::PointD(480, 240)); + testPoints.push_back(m2::PointD(520, 369)); + testPoints.push_back(m2::PointD(620, 370)); + + AddTest(testPoints, testPattern, yg::Color(255, 0, 0, 255), 1); + } + }; + + struct TestDrawPathSolid2PX : TestDrawPathBase + { + typedef TestDrawPathBase base_t; + + void Init() + { + base_t::Init(); + + std::vector testPoints; + std::vector testPattern; + + testPoints.push_back(m2::PointD(120, 200)); + testPoints.push_back(m2::PointD(180, 140)); + testPoints.push_back(m2::PointD(220, 269)); + testPoints.push_back(m2::PointD(320, 270)); + + AddTest(testPoints, testPattern, yg::Color(255, 0, 0, 255), 2); + + testPattern.push_back(20); + testPattern.push_back(20); + testPattern.push_back(20); + testPattern.push_back(20); + + testPoints.clear(); + testPoints.push_back(m2::PointD(420, 300)); + testPoints.push_back(m2::PointD(480, 240)); + testPoints.push_back(m2::PointD(520, 369)); + testPoints.push_back(m2::PointD(620, 370)); + + AddTest(testPoints, testPattern, yg::Color(255, 0, 0, 255), 2); + } + }; + + + struct TestDrawPathSolid : TestDrawPathBase + { + typedef TestDrawPathBase base_t; + + void Init() + { + base_t::Init(); + + std::vector testPoints; + std::vector testPattern; + +// testPoints.push_back(m2::PointD(120, 200)); +// testPoints.push_back(m2::PointD(180, 140)); + testPoints.push_back(m2::PointD(220, 269)); + testPoints.push_back(m2::PointD(320, 270)); + AddTest(testPoints, testPattern, yg::Color(255, 0, 0, 255), 40); testPattern.push_back(20); @@ -351,10 +452,10 @@ namespace testPattern.push_back(20); testPoints.clear(); - testPoints.push_back(m2::PointD(320, 300)); - testPoints.push_back(m2::PointD(380, 240)); - testPoints.push_back(m2::PointD(420, 369)); - testPoints.push_back(m2::PointD(520, 370)); + testPoints.push_back(m2::PointD(420, 300)); + testPoints.push_back(m2::PointD(480, 240)); + testPoints.push_back(m2::PointD(520, 369)); + testPoints.push_back(m2::PointD(620, 370)); AddTest(testPoints, testPattern, yg::Color(0, 255, 0, 255), 40); } @@ -765,7 +866,7 @@ namespace // UNIT_TEST_GL(TestDrawEmptySymbol); // UNIT_TEST_GL(TestDrawSingleSymbolAndSolidPath); // UNIT_TEST_GL(TestDrawString); - UNIT_TEST_GL(TestDrawStringWithFixedFont); +// UNIT_TEST_GL(TestDrawStringWithFixedFont); // UNIT_TEST_GL(TestDrawStringOnString); // UNIT_TEST_GL(TestDrawTextOnPath); // UNIT_TEST_GL(TestDrawTextOnPathWithOffset); @@ -778,7 +879,10 @@ namespace // UNIT_TEST_GL(TestDrawPathWithSkinPageMiss); // UNIT_TEST_GL(TestDrawPathWithOffset); // UNIT_TEST_GL(TestDrawPathJoin); + UNIT_TEST_GL(TestDrawPathSolid1PX); + UNIT_TEST_GL(TestDrawPathSolid2PX); // UNIT_TEST_GL(TestDrawPathSolid); + UNIT_TEST_GL(TestDrawPathSolidDiffWidth); // UNIT_TEST_GL(TestDrawPathSolidWithZ); // UNIT_TEST_GL(TestDrawPathSolidWithClipRect); // UNIT_TEST_GL(TestDrawUtilsRect);