Font fnt(font); fnt.Height((int)floor(height_dp)).Bold(object.bold).Italic(object.italic==1).Underline(object.selected); Pointf p(project.Transform(object.x,object.y)); double rot=object.rotation+rotation; rot+=object.no_rotate?-GetViewRotation():0; painter.Begin(); painter.Translate(p); if(rot) painter.Rotate(-rot*DEGR2RAD); switch(object.italic){ case 2: painter.Transform(Xform2D::Sheer(-0.3)); break; case 3: painter.Transform(Xform2D::Sheer(0.2)); break; } Vector<String> lines=Split(text,10); for(int row=0;row<lines.GetCount();row++){ Size sz=GetTextSize(lines[row],fnt); Pointf delta(0,0); switch(object.attpoint){ case 1: // 1 = Top left break; case 2: // Top Center delta.x=-sz.cx/2; break; case 3: // Top Right delta.x=-sz.cx; break; case 4: // Middle Left delta.y=-sz.cy/2; break; case 5: // Middle Center delta.y=-sz.cy/2; delta.x=-sz.cx/2; break; case 6: // Middle Right delta.y=-sz.cy/2; delta.x=-sz.cx; break; default: case 7: // Bottom Left delta.y=-sz.cy; break; case 8: // Bottom Center delta.y=-sz.cy; delta.x=-sz.cx/2; break; case 9: // Bottom Right delta.y=-sz.cy; delta.x=-sz.cx; break; } delta.y+=row*height_dp*1.5; // sz.cy painter.Text(delta,lines[row],fnt); } if(edgecolor.a) painter.Stroke(height_dp*0.1,edgecolor); painter.Fill(textcolor); painter.End();
void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); } #if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); Vector<float>& g = chp.glyph; #else const Vector<float>& g = ValueTo< Vector<float> >(v); #endif int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
Hi,
Thanks! I will check this too tomorrow!
Anyway, if this does not sort out easily, I can store the problematic sequence in a Painting and serialize it in a file for you to check out. (The same procedure as last time, when there was a complex Painter issue.)
Thanks,
Tom
void BufferPainter::CharacterOp(const Pointf& p, int ch, Font fnt) { LLOG("@ CharacterOp " << p << ", " << ch << ", " << fnt); #if 1 DoMove0(); PaintCharacter(*this, p, ch, fnt); #else move = current = EndPoint(p, false); auto& m = PathAdd<CharData>(CHAR); m.p = EndPoint(p, false); m.ch = ch; m.fnt = fnt; path_info->ischar = true; EvenOdd(); #endif }
struct GlyphKey { Font fnt; int chr; double tolerance; bool operator==(const GlyphKey& b) const { DLOG("*** operator=="); DDUMP(fnt); DDUMP(b.fnt); DDUMP(chr); DDUMP(b.chr); return fnt == b.fnt && chr == b.chr && tolerance == b.tolerance; } unsigned GetHashValue() const { return CombineHash(fnt, chr, tolerance); } }; struct sMakeGlyph : LRUCache<Value, GlyphKey>::Maker { GlyphKey gk; GlyphKey Key() const { return gk; } int Make(Value& v) const { GlyphPainter gp; gp.move = gp.pos = Null; gp.tolerance = gk.tolerance; DLOG("*** Make"); DDUMP(GetHashValue(gk)); DDUMP(gk.fnt); DDUMP(gk.chr); PaintCharacter(gp, Pointf(0, 0), gk.chr, gk.fnt); int sz = gp.glyph.GetCount() * 4; v = RawPickToValue(pick(gp.glyph)); return sz; } }; void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { DLOG("==== ApproximateChar " << ch << " " << fnt); PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; DDUMP(GetHashValue(h.gk)); v = cache.Get(h); } #if 0 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); const Vector<float>& g = chp.glyph; #else const Vector<float>& g = ValueTo< Vector<float> >(v); #endif int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
----- Painter ( GUI PROTECT MSC17X64 MSC WIN32 ) (8 / 32) RenderChar.cpp C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(96): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(97): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(98): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(99): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(100): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(116): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(117): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(118): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(119): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(132): error C2018: unknown character '0x40' C:\upp-12610\upp.src\uppsrc\Painter\RenderChar.cpp(140): error C2018: unknown character '0x40' Painter: 1 file(s) built in (0:00.90), 902 msecs / file, duration = 910 msecs, parallelization 0%
Hi,
PS: The DLOG and DDUMP cause errors in RELEASE mode, but not in DEBUG mode though:
Tom1 wrote on Fri, 11 January 2019 11:46Hi,
PS: The DLOG and DDUMP cause errors in RELEASE mode, but not in DEBUG mode though:
That is by design. It forces you to remove LOGs that you have inserted for debugging purposes. Not ideal, but better than to have logfile overcrowded with forgotten debugging info that you have needed 5 years ago
struct GlyphKey { Font fnt; int chr; double tolerance; bool operator==(const GlyphKey& b) const { DLOG("*** operator=="); DDUMP(fnt); DDUMP(b.fnt); DDUMP(chr); DDUMP(b.chr); return fnt == b.fnt && chr == b.chr && tolerance == b.tolerance; } unsigned GetHashValue() const { return CombineHash(fnt, chr, tolerance); } }; struct sMakeGlyph : LRUCache<Value, GlyphKey>::Maker { GlyphKey gk; GlyphKey Key() const { return gk; } int Make(Value& v) const { GlyphPainter gp; gp.move = gp.pos = Null; gp.tolerance = gk.tolerance; DLOG("*** Make"); DDUMP(GetHashValue(gk)); DDUMP(gk.fnt); DDUMP(gk.chr); PaintCharacter(gp, Pointf(0, 0), gk.chr, gk.fnt); int sz = gp.glyph.GetCount() * 4; v = RawPickToValue(pick(gp.glyph)); return sz; } }; void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { DLOG("==== ApproximateChar " << ch << " " << fnt); PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; DDUMP(GetHashValue(h.gk)); v = cache.Get(h); DDUMP(ValueTo< Vector<float> >(v)); } #if 0 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); const Vector<float>& g = chp.glyph; #else const Vector<float>& g = ValueTo< Vector<float> >(v); #endif int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
==== ApproximateChar 74 <Arial:27> GetHashValue(h.gk) = 3041017192 *** operator== fnt = <Arial:27> b.fnt = <Arial:5 Italic> chr = 74 b.chr = 214 *** operator== fnt = <Arial:27> b.fnt = <Arial:27> chr = 74 b.chr = 74 ValueTo< Vector<float> >(v) = [9.99999984824321e30, 1, 17.46875, 4, 17, 4.39453125, 19.908203125, 5.296875, 21.7265625, 6.671875, 22.681640625, 8.484375, 23, 9.892578125, 22.84765625, 11.0859375, 22.390625, 12.00390625, 21.671875, 12.5859375, 20.734375, 13, 17.390625, 13, -1, 16, -1, 16, 17.09375, 15.79296875, 20.046875, 15.171875, 22.25, 14.1015625, 23.861328125, 12.546875, 25.0390625, 10.59375, 25.759765625, 8.328125, 26, 6.63671875, 25.86572265625, 5.15625, 25.462890625, 3.88671875, 24.79150390625, 2.828125, 23.8515625, 1.9990234375, 22.64697265625, 1.41796875, 21.181640625, 1, 17.46875] ==== ApproximateChar 111 <Arial:27> GetHashValue(h.gk) = 3041016509 *** operator== fnt = <Arial:27> b.fnt = <Arial:27> chr = 111 b.chr = 111 ValueTo< Vector<float> >(v) = [9.99999984824321e30, 1, 18.5, 1.1337890625, 16.63671875, 1.53515625, 15.03125, 2.2041015625, 13.68359375, 3.140625, 12.59375, 5.12890625, 11.3984375, 7.5, 11, 10.109375, 11.484375, 12.1875, 12.9375, 13.546875, 15.24609375, 14, 18.296875, 13.798828125, 20.76953125, 13.1953125, 22.65625, 12.208984375, 24.0703125, 10.859375, 25.125, 9.25390625, 25.78125, 7.5, 26, 4.87109375, 25.517578125, 2.796875, 24.0703125, 2.0107421875, 22.99853515625, 1.44921875, 21.712890625, 1, 18.5, 9.99999984824321e30, 3, 18.5, 3.3203125, 20.90625, 4.28125, 22.625, 5.7265625, 23.65625, 7.5, 24, 9.2734375, 23.654296875, 10.71875, 22.6171875, 11.6796875, 20.873046875, 12, 18.40625, 11.677734375, 16.0703125, 10.7109375, 14.375, 9.263671875, 13.34375, 7.5, 13, 5.7265625, 13.341796875, 4.28125, 14.3671875, 3.3203125, 16.083984375, 3, 18.5] ==== ApproximateChar 101 <Arial:27> GetHashValue(h.gk) = 3041016535 *** operator== fnt = <Arial:27> b.fnt = <Arial:27> chr = 101 b.chr = 101 ValueTo< Vector<float> >(v) = [9.99999984824321e30, 11.96875, 22, 13.9375, 22, 13.107421875, 23.69140625, 11.7734375, 24.953125, 9.970703125, 25.73828125, 7.734375, 26, 4.94921875, 25.515625, 2.8125, 24.0625, 1.453125, 21.734375, 1, 18.625, 1.458984375, 15.40625, 2.03271484375, 14.1015625, 2.8359375, 13, 4.943359375, 11.5, 7.59375, 11, 10.162109375, 11.482421875, 12.2109375, 12.9296875, 13.552734375, 15.251953125, 14, 18.359375, 13.984375, 19, 3, 19, 3.44921875, 21.138671875, 4.484375, 22.7109375, 5.9765625, 23.677734375, 7.796875, 24, 10.3125, 23.5234375, 11.25, 22.904296875, 11.96875, 22, 9.99999984824321e30, 3, 17, 12, 17, 11.6484375, 15.46484375, 10.96875, 14.359375, 9.46484375, 13.33984375, 7.578125, 13, 5.8515625, 13.271484375, 4.421875, 14.0859375, 3.42578125, 15.357421875, 3, 17]
Hi,
Interestingly it always has two operator== hash compares until a match is found. (I repeated the rendering multiple times to find the spot in log more easily.) Is this multiple hash match affecting the caching in some way?
PS: When a wrong cache item is picked for a character, it may have wrong size and wrong Italic/Normal coding, but it always is the correct
void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); DDUMP(ValueTo< Vector<float> >(v)); #if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); DDUMP(chp.glyph); ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif } const Vector<float>& g = ValueTo< Vector<float> >(v); int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
Hi,
The first message in this thread portrays that: The 'Örviksudden' should have been in italic, but was instead shown with straight up 'Ö', if you look at it carefully. I have seen this in other tests too occasionally, so I can confirm it.
Still no clues... Anyway, let us modify logging a bit:
void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); DDUMP(ValueTo< Vector<float> >(v)); #if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); DDUMP(chp.glyph); ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif } const Vector<float>& g = ValueTo< Vector<float> >(v); int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
(If error is here, it should also assert....)
#include <CtrlLib/CtrlLib.h> #include <Painter/Painter.h> using namespace Upp; class PainterText : public TopWindow { public: Painting p; FileSel fs; BufferPainter bpainter; double scale = 2; void Open(){ if(fs.ExecuteOpen("Select a painting to view")){ p.Clear(); p.Serialize(FileIn(fs.Get())); } } virtual void MouseWheel(Point p, int zdelta, dword keyflags) { if(zdelta < 0) scale *= 0.8; else scale /= 0.8; Refresh(); } virtual bool Key(dword key, int count){ Refresh(); switch(key){ case K_CTRL_O: Open(); return true; } return false; } typedef PainterText CLASSNAME; PainterText(){ Sizeable(); p.Serialize(FileIn("C:/xxx/PainteTest/T5.painting")); } virtual void Paint(Draw &draw){ ImageBuffer ib(GetSize()); { bpainter.Create(ib); bpainter.Co(false); bpainter.PreClipDashed(); bpainter.Clear(White()); bpainter.EvenOdd(); bpainter.Scale(scale); bpainter.Paint(p); bpainter.Finish(); } SetSurface(draw,Rect(ib.GetSize()),ib,ib.GetSize(),Point(0,0)); } }; GUI_APP_MAIN { PainterText().Run(); }
mirek wrote on Fri, 11 January 2019 16:43
#if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); DDUMP(chp.glyph); ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
(If error is here, it should also assert....)
Strangely enough, this did not exhibit the problem at all. There are no buggy letters, as far as I could tell from looking around the map for 30 minutes. No asserts either, but that is to be expected as the problem did not surface here...
#if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); // <<-- Required for correct rendering // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
Hi Mirek,
OK, here's the situation. Using PROTECT or not does not have any effect on this issue. Neither does the compiler; MSBT17 and MSBT17x64 both work the same.
After starting to comment out the lines from the bottom of the sequence, PaintCharacter() proved to be the line required for correct operation. Commenting it out it brought the problem back:
#if 1 GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; PaintCharacter(chp, Pointf(0, 0), ch, fnt); // <<-- Required for correct rendering // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
Any suggestions, how I should proceed?
Best regards,
Tom
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; extern HFONT GetWin32Font(Font fnt, int angle); GetWin32Font(fnt, 0); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
...
Try this:
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; extern HFONT GetWin32Font(Font fnt, int angle); GetWin32Font(fnt, 0); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
GetWin32Font is called by PaintCharacter and has some caching inside too, maybe that is the one that is causing the trouble. Of course, if calling it fixes the problem, try to comment and uncomment...
mirek wrote on Mon, 14 January 2019 11:13...
Try this:
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; extern HFONT GetWin32Font(Font fnt, int angle); GetWin32Font(fnt, 0); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
GetWin32Font is called by PaintCharacter and has some caching inside too, maybe that is the one that is causing the trouble. Of course, if calling it fixes the problem, try to comment and uncomment...
This does not fix the issue.
BR,
Tom
void PaintCharacter(Painter& sw, const Pointf& p, int chr, Font font) { GlyphInfo gi = GetGlyphInfo(font, chr); PaintCharPath pw; pw.sw = &sw; if(gi.IsNormal()) font.Render(pw, p.x, p.y, chr); /* else if(gi.IsReplaced()) { Font fnt = font; fnt.Face(gi.lspc); fnt.Height(gi.rspc); fnt.Render(pw, p.x, p.y + font.GetAscent() - fnt.GetAscent(), chr); } else if(gi.IsComposed()) { ComposedGlyph cg; Compose(font, chr, cg); font.Render(pw, p.x, p.y, cg.basic_char); sw.Div(); cg.mark_font.Render(pw, p.x + cg.mark_pos.x, p.y + cg.mark_pos.y, cg.mark_char); }*/ sw.EvenOdd(true); }
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; GetGlyphInfo(fnt, ch); // GetWin32Font(fnt, 0); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); DDUMP(chp.glyph); ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
Another idea to test:
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; GetGlyphInfo(fnt, ch); // GetWin32Font(fnt, 0); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); DDUMP(chp.glyph); ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
Does this fix the issue? (GetGlyphInfo is another caching function called from PaintCharacter).
Mirek
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; struct PaintCharPath : FontGlyphConsumer { Painter *sw; virtual void Move(Pointf p) { sw->Move(p); } virtual void Line(Pointf p) { sw->Line(p); } virtual void Quadratic(Pointf p1, Pointf p2) { sw->Quadratic(p1, p2); } virtual void Cubic(Pointf p1, Pointf p2, Pointf p3) { sw->Cubic(p1, p2, p3); } virtual void Close() { sw->Close(); } } pw; pw.sw = &chp; fnt.Render(pw, 0, 0, ch); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
Tried. Now the error appears if "fnt.Render(pw, 0, 0, ch);" is commented out from the above.
BR, Tom
#ifdef 1 DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; struct PaintCharPath : FontGlyphConsumer { Painter *sw; virtual void Move(Pointf p) { sw->Move(p); } virtual void Line(Pointf p) { sw->Line(p); } virtual void Quadratic(Pointf p1, Pointf p2) { sw->Quadratic(p1, p2); } virtual void Cubic(Pointf p1, Pointf p2, Pointf p3) { sw->Cubic(p1, p2, p3); } virtual void Close() { sw->Close(); } } pw; pw.sw = &chp; void RenderCharacterSys(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt); RenderCharacterSys(pw, 0, 0, ch, fnt); // fnt.Render(pw, 0, 0, ch); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif
double fx_to_dbl(const FIXED& p); Pointf fx_to_dbl(const Pointf& pp, const POINTFX& p); struct sMakeGlyph : LRUCache<Value, GlyphKey>::Maker { GlyphKey gk; GlyphKey Key() const { return gk; } int Make(Value& v) const { GlyphPainter gp; gp.move = gp.pos = Null; gp.tolerance = gk.tolerance; PaintCharacter(gp, Pointf(0, 0), gk.chr, gk.fnt); int sz = gp.glyph.GetCount() * 4; v = RawPickToValue(pick(gp.glyph)); return sz; } }; void RenderCharPath2(const char* gbuf, unsigned total_size, FontGlyphConsumer& sw, double xx, double yy) { const char* cur_glyph = gbuf; const char* end_glyph = gbuf + total_size; Pointf pp(xx, yy); while(cur_glyph < end_glyph) { const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; const char* end_poly = cur_glyph + th->cb; const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); sw.Move(fx_to_dbl(pp, th->pfxStart)); while(cur_poly < end_poly) { const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; if (pc->wType == TT_PRIM_LINE) for(int i = 0; i < pc->cpfx; i++) sw.Line(fx_to_dbl(pp, pc->apfx[i])); if (pc->wType == TT_PRIM_QSPLINE) for(int u = 0; u < pc->cpfx - 1; u++) { Pointf b = fx_to_dbl(pp, pc->apfx[u]); Pointf c = fx_to_dbl(pp, pc->apfx[u + 1]); if (u < pc->cpfx - 2) c = Mid(b, c); sw.Quadratic(b, c); } cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; } sw.Close(); cur_glyph += th->cb; } } HFONT GetWin32Font(Font fnt, int angle); HDC Win32_IC(); void RenderCharacterSys2(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt) { HFONT hfont = GetWin32Font(fnt, 0); if(hfont) { HDC hdc = Win32_IC(); HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont); GLYPHMETRICS gm; MAT2 m_matrix; memset(&m_matrix, 0, sizeof(m_matrix)); m_matrix.eM11.value = 1; m_matrix.eM22.value = 1; int gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE, &gm, 0, NULL, &m_matrix); if(gsz < 0) return; StringBuffer gb(gsz); gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE, &gm, gsz, ~gb, &m_matrix); if(gsz < 0) return; RenderCharPath2(~gb, gsz, sw, x, y + fnt.GetAscent()); ::SelectObject(hdc, ohfont); } } void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); Value v; INTERLOCKED { PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<Value, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); #ifdef _DEBUG DLOG("==== ApproximateChar " << ch << " " << (char)ch << " " << fnt << ", tolerance: " << tolerance); DDUMP(ValueTo< Vector<float> >(v)); GlyphPainter chp; chp.move = chp.pos = Null; chp.tolerance = tolerance; struct PaintCharPath : FontGlyphConsumer { Painter *sw; virtual void Move(Pointf p) { sw->Move(p); } virtual void Line(Pointf p) { sw->Line(p); } virtual void Quadratic(Pointf p1, Pointf p2) { sw->Quadratic(p1, p2); } virtual void Cubic(Pointf p1, Pointf p2, Pointf p3) { sw->Cubic(p1, p2, p3); } virtual void Close() { sw->Close(); } } pw; pw.sw = &chp; RenderCharacterSys2(pw, 0, 0, ch, fnt); // fnt.Render(pw, 0, 0, ch); // PaintCharacter(chp, Pointf(0, 0), ch, fnt); // DDUMP(chp.glyph); // ASSERT(ValueTo< Vector<float> >(v) == chp.glyph); #endif } const Vector<float>& g = ValueTo< Vector<float> >(v); int i = 0; while(i < g.GetCount()) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
// RenderCharPath2(~gb, gsz, sw, x, y + fnt.GetAscent());
struct sMakeGlyph : LRUCache<String, GlyphKey>::Maker { GlyphKey gk; GlyphKey Key() const { return gk; } int Make(String& v) const { GlyphPainter gp; gp.move = gp.pos = Null; gp.tolerance = gk.tolerance; PaintCharacter(gp, Pointf(0, 0), gk.chr, gk.fnt); int sz = gp.glyph.GetCount() * 4; v.Set((char *)gp.glyph.begin(), sizeof(float) * gp.glyph.GetCount()); return sz; } }; void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); String v; INTERLOCKED { PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<String, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); } int i = 0; int count = v.GetCount() / sizeof(float); const float *g = (const float *)~v; while(i < count) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
Also try
RenderCharPath2(~gb, gsz, sw, x, y/* + fnt.GetAscent()*/);
void RenderCharPath2(const char* gbuf, unsigned total_size, FontGlyphConsumer& sw, double xx, double yy) { const char* cur_glyph = gbuf; const char* end_glyph = gbuf + total_size; Pointf pp(xx, yy); while(cur_glyph < end_glyph) { const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; const char* end_poly = cur_glyph + th->cb; const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); sw.Move(fx_to_dbl(pp, th->pfxStart)); /* while(cur_poly < end_poly) { const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; if (pc->wType == TT_PRIM_LINE) for(int i = 0; i < pc->cpfx; i++) sw.Line(fx_to_dbl(pp, pc->apfx[i])); if (pc->wType == TT_PRIM_QSPLINE) for(int u = 0; u < pc->cpfx - 1; u++) { Pointf b = fx_to_dbl(pp, pc->apfx[u]); Pointf c = fx_to_dbl(pp, pc->apfx[u + 1]); if (u < pc->cpfx - 2) c = Mid(b, c); sw.Quadratic(b, c); } cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; } */ sw.Close(); cur_glyph += th->cb; } }
void RenderCharPath2(const char* gbuf, unsigned total_size, FontGlyphConsumer& sw, double xx, double yy) { const char* cur_glyph = gbuf; const char* end_glyph = gbuf + total_size; Pointf pp(xx, yy); while(cur_glyph < end_glyph) { const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; const char* end_poly = cur_glyph + th->cb; const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); sw.Move(fx_to_dbl(pp, th->pfxStart)); while(cur_poly < end_poly) { const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; /* if (pc->wType == TT_PRIM_LINE) for(int i = 0; i < pc->cpfx; i++) sw.Line(fx_to_dbl(pp, pc->apfx[i])); if (pc->wType == TT_PRIM_QSPLINE) for(int u = 0; u < pc->cpfx - 1; u++) { Pointf b = fx_to_dbl(pp, pc->apfx[u]); Pointf c = fx_to_dbl(pp, pc->apfx[u + 1]); if (u < pc->cpfx - 2) c = Mid(b, c); sw.Quadratic(b, c); } */ cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; } sw.Close(); cur_glyph += th->cb; } }
This shows the problem:
BR, Tom
Thinking about it, I have suspiction that maybe RawPickToValue might have to do something with all that...
What about this:
struct sMakeGlyph : LRUCache<String, GlyphKey>::Maker { GlyphKey gk; GlyphKey Key() const { return gk; } int Make(String& v) const { GlyphPainter gp; gp.move = gp.pos = Null; gp.tolerance = gk.tolerance; PaintCharacter(gp, Pointf(0, 0), gk.chr, gk.fnt); int sz = gp.glyph.GetCount() * 4; v.Set((char *)gp.glyph.begin(), sizeof(float) * gp.glyph.GetCount()); return sz; } }; void ApproximateChar(LinearPathConsumer& t, Pointf at, int ch, Font fnt, double tolerance) { PAINTER_TIMING("ApproximateChar"); String v; INTERLOCKED { PAINTER_TIMING("ApproximateChar::Fetch"); static LRUCache<String, GlyphKey> cache; cache.Shrink(500000); sMakeGlyph h; h.gk.fnt = fnt; h.gk.chr = ch; h.gk.tolerance = tolerance; v = cache.Get(h); } int i = 0; int count = v.GetCount() / sizeof(float); const float *g = (const float *)~v; while(i < count) { Pointf p; p.x = g[i++]; if(p.x > 1e30) { p.x = g[i++]; p.y = g[i++]; t.Move(p + at); } else { PAINTER_TIMING("ApproximateChar::Line"); p.y = g[i++]; t.Line(p + at); } } }
Have you tried this on another computer? (Blaming HW is the last thing I usually do, but this is way too weird...)
Mirek
According to Windows Task Manager, there are 30 threads running on this process. But my GUI is simply running on main thread. There should be nothing graphical going on in any of my background threads.
Here's an interesting part: I said previously that ST/MT in Painter did not make any difference. Well, it did not when I had started the program originally with Painter MT enabled and then switched 'live' to ST to test for the error effect. BUT: Now I started with Painter fixed in ST and: Error is gone... Sounds like in my original MT/ST test the font caching in Painter had already happened in MT before I switched it to ST.
I need to dig deeper on this.
BR, Tom
Buffer<ClippingLine> BufferPainter::RenderPath(double width, Event<One<SpanSource>&> ss, const RGBA& color) { PAINTER_TIMING("RenderPath"); Buffer<ClippingLine> newclip; if(width == FILL) Close(); current = Null; if(width == 0 || !ss && color.a == 0 && width >= FILL) return newclip; if(pathattr.mtx_serial != preclip_mtx_serial) { if(dopreclip) { Pointf tl, br, a; Xform2D imx = Inverse(pathattr.mtx); tl = br = imx.Transform(0, 0); a = imx.Transform(size.cx, 0); tl = min(a, tl); br = max(a, br); a = imx.Transform(0, size.cy); tl = min(a, tl); br = max(a, br); a = imx.Transform(size.cx, size.cy); tl = min(a, tl); br = max(a, br); preclip = Rectf(tl, br); } else preclip = Null; regular = pathattr.mtx.IsRegular(); preclip_mtx_serial = pathattr.mtx_serial; } if(co) { if(width >= FILL && !ss && !alt && findarg(mode, MODE_ANTIALIASED, MODE_SUBPIXEL) >= 0 && !path_info->ischar) {
Have you tried this on another computer? (Blaming HW is the last thing I usually do, but this is way too weird...)
Mirek
Try this pls:
Buffer<ClippingLine> BufferPainter::RenderPath(double width, Event<One<SpanSource>&> ss, const RGBA& color) { PAINTER_TIMING("RenderPath"); Buffer<ClippingLine> newclip; if(width == FILL) Close(); current = Null; if(width == 0 || !ss && color.a == 0 && width >= FILL) return newclip; if(pathattr.mtx_serial != preclip_mtx_serial) { if(dopreclip) { Pointf tl, br, a; Xform2D imx = Inverse(pathattr.mtx); tl = br = imx.Transform(0, 0); a = imx.Transform(size.cx, 0); tl = min(a, tl); br = max(a, br); a = imx.Transform(0, size.cy); tl = min(a, tl); br = max(a, br); a = imx.Transform(size.cx, size.cy); tl = min(a, tl); br = max(a, br); preclip = Rectf(tl, br); } else preclip = Null; regular = pathattr.mtx.IsRegular(); preclip_mtx_serial = pathattr.mtx_serial; } if(co) { if(width >= FILL && !ss && !alt && findarg(mode, MODE_ANTIALIASED, MODE_SUBPIXEL) >= 0 && !path_info->ischar) {
(The difference is && !path_info->ischar in the last line).
Also, is there a difference DEBUG vs RELEASE?
Not a single false character in DEBUG mode either.
I should have found something already if the problem still existed.
So, is this the fix or means for problem isolation?
Best regards,
Tom
void Painter::TextOp(const Pointf& p, const wchar *text, Font fnt, int n, const double *dx) { if(n == 0) { Move(0, 0); return; } FontInfo fi = fnt.Info(); double x = p.x; while(n) { int ch = *text++; Character(x, p.y, ch, fnt); // Div(); if(dx) x += *dx++; else x += fi[ch]; n--; } if(fnt.IsUnderline() || fnt.IsStrikeout()) { double a = fnt.GetAscent(); double cy = max(a / 16, 1.0); double cx = x - p.x; if(fnt.IsUnderline()) Rectangle(p.x, p.y + a + cy, cx, cy); if(fnt.IsStrikeout()) Rectangle(p.x, p.y + 2 * a / 3, cx, cy); } }
void BufferPainter::RenderPathSegments(LinearPathConsumer *g, const Vector<byte>& path, const SimpleAttr *attr, double tolerance) { Pointf pos = Pointf(0, 0); const byte *data = path.begin(); const byte *end = path.end(); while(data < end) { const LinearData *d = (LinearData *)data; switch(d->type) { case MOVE: { g->Move(pos = attr ? attr->mtx.Transform(d->p) : d->p); data += sizeof(LinearData); break; } case LINE: { PAINTER_TIMING("LINE"); g->Line(pos = attr ? attr->mtx.Transform(d->p) : d->p); data += sizeof(LinearData); break; } case QUADRATIC: { PAINTER_TIMING("QUADRATIC"); const QuadraticData *d = (QuadraticData *)data; if(attr) { Pointf p = attr->mtx.Transform(d->p); ApproximateQuadratic(*g, pos, attr->mtx.Transform(d->p1), p, tolerance); pos = p; } else { ApproximateQuadratic(*g, pos, d->p1, d->p, tolerance); pos = d->p; } data += sizeof(QuadraticData); break; } case CUBIC: { PAINTER_TIMING("CUBIC"); const CubicData *d = (CubicData *)data; if(attr) { Pointf p = attr->mtx.Transform(d->p); ApproximateCubic(*g, pos, attr->mtx.Transform(d->p1), attr->mtx.Transform(d->p2), p, tolerance); pos = p; } else { ApproximateCubic(*g, pos, d->p1, d->p2, d->p, tolerance); pos = d->p; } data += sizeof(CubicData); break; } case CHAR: { const CharData *ch = (CharData *)data; INTERLOCKED ApproximateChar(*g, ch->p, ch->ch, ch->fnt, tolerance); data += sizeof(CharData); break; } default: NEVER(); g->End(); return; } } g->End(); }
No, false positive. The problem is still there even after the "void Painter::TextOp(const Pointf& p, const wchar *text, Font fnt, int n, const double *dx)" -fix.
BR, Tom
One potential reason for finding this issue is that I had just recently reached a point in my own code where I started to rotate the texts according to the original scale and rotation, both of which change constantly over the projection surface for maps where a change from one map projection to another has been carried out.
In effect this has caused an overwhelming increase in cached fonts here in my application.
Tom1 wrote on Mon, 14 January 2019 17:21One potential reason for finding this issue is that I had just recently reached a point in my own code where I started to rotate the texts according to the original scale and rotation, both of which change constantly over the projection surface for maps where a change from one map projection to another has been carried out.
In effect this has caused an overwhelming increase in cached fonts here in my application.
Rotated texts in Painter are rotated by Painter matrix, they are cached in zero angle position...
Anyway, good info, I will think about this all...
Mirek
mirek wrote on Mon, 14 January 2019 18:45Tom1 wrote on Mon, 14 January 2019 17:21One potential reason for finding this issue is that I had just recently reached a point in my own code where I started to rotate the texts according to the original scale and rotation, both of which change constantly over the projection surface for maps where a change from one map projection to another has been carried out.
In effect this has caused an overwhelming increase in cached fonts here in my application.
Rotated texts in Painter are rotated by Painter matrix, they are cached in zero angle position...
Anyway, good info, I will think about this all...
Mirek
Well, yes, they are indeed cached in zero angle position. I should have expressed myself more clearly to make the point: Both fine scaling and fine rotation changes ultimately have an effect on double tolerance value solved using the Painter matrix scale and stored as caching key. This is also why rotating the image sometimes corrected the problem. Please see my early screenshots in this thread. (The changes would have been just at a magnitude of rounding errors i.e. just some LSBs.)
BR, Tom
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; /* CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } };*/ for(int i = 0; i < jobcount; i++) { CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } FinishFillJob(); fillcount = jobcount; Swap(cofill, cojob); // Swap to keep allocated rasters (instead of pick)
BufferPainter::PathJob::PathJob(Rasterizer& rasterizer, double width, const PathInfo *path_info, const SimpleAttr& attr, const Rectf& preclip, bool isregular) : trans(attr.mtx) { evenodd = attr.evenodd; regular = isregular && width < 0 && !path_info->ischar; g = &rasterizer; if(!IsNull(preclip.left) && !path_info->ischar) { double ex = max(width, 0.0) * (1 + attr.miter_limit); if(path_info->path_max.y + ex < preclip.top || path_info->path_min.y - ex > preclip.bottom || path_info->path_max.x + ex < preclip.left || path_info->path_min.x - ex > preclip.right) { preclipped = true; return; } } preclipped = false; if(regular) tolerance = 0.3; else { trans.target = g; g = &trans; tolerance = 0.3 / attr.mtx.GetScale(); } tolerance = 0.3; // add this...
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; { CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } }; } FinishFillJob();
OK, now I see possible bug, hard to say it is related, but please test:
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; { CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } }; } FinishFillJob();
(The difference is that CoWork is now inside block).
EDIT: Perhaps check the possible fix first - the last post "OK, now I see possible bug, hard to say it is related, but please test:" and do testing only if it fails.
To test:
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; /* CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } };*/ for(int i = 0; i < jobcount; i++) { CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } FinishFillJob(); fillcount = jobcount; Swap(cofill, cojob); // Swap to keep allocated rasters (instead of pick)
(Changing the path rendering to run in ST)
I do not know if this has anything to do with it, but I have noticed that I cannot pass reference variables as parameters to CoWork::Do() -called functions. Compiler does not complain, but the code simply does not work correctly. I have had to switch to using pointers as parameters instead of reference variables in such functions.
Hi,
As for forcing "tolerance = 0.3;" likely decreases the number of cache entries to a fraction of the original in this case, the likelihood to see any issues drops equally. And so it seems: I cannot see a single erroneous character.
Then again, I can check about 1000 names in a reasonable time on screen and if the problem is there, I will see just a few errors. I think that it becomes just a few errors in a million now, so I'm not 'lucky enough' to find them.
BR, Tom
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; INTERLOCKED { CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } } };
Tom1 wrote on Tue, 15 January 2019 08:53
I do not know if this has anything to do with it, but I have noticed that I cannot pass reference variables as parameters to CoWork::Do() -called functions. Compiler does not complain, but the code simply does not work correctly. I have had to switch to using pointers as parameters instead of reference variables in such functions.
That should not be issue here. Now all depends on details, but you always have to be careful that referenced variable exists until CoWork Finish or destructor... If you think that you have non-working example of different nature, please start a new thread a post it...
Mirek
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; CoWork co; co * [&] { for(;;) { int i = co.Next(); if(i >= jobcount) break; INTERLOCKED { CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } } };
If problem disappears, try to move INTERLOCKED down line by line (until if) to see when/if it reappears.
I am also trying to reproduce the issue here; anyway the paintings I have here only has a couple o texts in it.
Now I am not sure you have any level-of-detail systeme there, but if not, would it be possible to send me extremely zoomed out painting file so that it has more texts in it?
(That said, when testing, do you start with such zoomed-out situation?)
What about locking the whole lambda body?
Also to test in the same loop:
PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, false);
(change the last parameter to 'false')
void BufferPainter::FinishPathJob() { if(jobcount == 0) return; CoWork co; co * [&] { INTERLOCKED { for(;;) { int i = co.Next(); if(i >= jobcount) break; CoJob& b = cojob[i]; b.rasterizer.Reset(); PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, b.regular); if(!j.preclipped) { b.evenodd = j.evenodd; BufferPainter::RenderPathSegments(j.g, b.path_info->path[b.subpath], j.regular ? &b.attr : NULL, j.tolerance); } } } };
#include <CtrlLib/CtrlLib.h> #include <Painter/Painter.h> using namespace Upp; class PainterText : public TopWindow { public: Painting p; FileSel fs; BufferPainter bpainter; double scale = 0.4; int rotation = 0; Pointf translate = Pointf(0, 0); Pointf start; void Open(){ if(fs.ExecuteOpen("Select a painting to view")) { p.Clear(); p.Serialize(FileIn(fs.Get())); } } virtual void LeftDown(Point p, dword) { start = (Pointf)p * scale; SetCapture(); } virtual void MouseMove(Point p, dword keyflags) { if(HasCapture()) { Pointf pos = (Pointf)p * scale; translate += (pos - start); start = pos; Refresh(); } } virtual void MouseWheel(Point p, int zdelta, dword keyflags) { if(keyflags & K_CTRL) rotation += sgn(zdelta); else { if(zdelta < 0) scale *= 0.9; else scale /= 0.9; } Refresh(); } virtual bool Key(dword key, int count){ Refresh(); switch(key){ case K_CTRL_O: Open(); return true; } return false; } typedef PainterText CLASSNAME; PainterText(){ Sizeable(); p.Serialize(FileIn("C:/xxx/PainteTest/T5.painting")); } virtual void Paint(Draw &draw){ ImageBuffer ib(GetSize()); { bpainter.Create(ib); bpainter.Co(true); bpainter.PreClipDashed(); bpainter.Clear(White()); bpainter.EvenOdd(); bpainter.Scale(scale); bpainter.Rotate(rotation * .1); bpainter.Translate(translate); bpainter.Paint(p); bpainter.Finish(); } SetSurface(draw,Rect(ib.GetSize()),ib,ib.GetSize(),Point(0,0)); } }; GUI_APP_MAIN { PainterText().Run(); }
What about locking the whole lambda body?
Also to test in the same loop:
PathJob j(b.rasterizer, b.width, b.path_info, b.attr, b.preclip, false);
(change the last parameter to 'false')
CharEntry fc_cache[512]; GlyphInfo GetGlyphInfo(Font font, int chr) { INTERLOCKED { font.RealizeStd(); unsigned hash = GlyphHash(font, chr); CharEntry& e = fc_cache[hash & 511]; if(e.font != font.AsInt64() || e.chr != chr) e = GetGlyphEntry(font, chr, hash); return e.info; } }
FontEntry fi_cache[63]; const CommonFontInfo& GetFontInfo(Font font) { INTERLOCKED { font.RealizeStd(); unsigned hash = FoldHash(font.GetHashValue()) % 63; FontEntry& e = fi_cache[hash]; if(e.font != font.AsInt64()) { Mutex::Lock __(sFontLock); e.font = font.AsInt64(); e.info = GetFontInfoSys(font); } return e.info; } }
virtual void LeftDown(Point p, dword) { start = (Pointf)p / scale; SetCapture(); } virtual void MouseMove(Point p, dword keyflags) { if(HasCapture()) { Pointf pos = (Pointf)p / scale; translate += (pos - start); start = pos; Refresh(); } }
PainterText().MinimizeBox().MaximizeBox().Sizeable().Run();
As to this issue, is it possible that all other texts are using your custom "Sheer" italic?
It actually looks like setting tolerance to 0.3 is not a good idea...
EDIT: tolerance is ok
void Painter::TextOp(const Pointf& p, const wchar *text, Font fnt, int n, const double *dx) { DDUMP(WString(text, n));
Is it by any means possible that missing letter are from those 'ugly' texts?
I have 1 h 8'...
Anyway, I need to pull back that 'Bold' from some place names. E.g. 'Paloniemi', which is rendered ugly, is just slightly larger straight up text than the more common place names which are italic... not even bold!
Best regards,
Tom
Hi,
Removal of thread__ for fc_cache and fi_cache do not help. The issue is still there.
-
As for the testcode, I could not spot any false characters there, but the granularity due to significant zooming ruins part of the larger texts.
BR, Tom
So I guess the storage format and precision in Painting is dependent on font size.
Tom1 wrote on Tue, 15 January 2019 11:19Hi,
Removal of thread__ for fc_cache and fi_cache do not help. The issue is still there.
-
As for the testcode, I could not spot any false characters there, but the granularity due to significant zooming ruins part of the larger texts.
BR, Tom
Hi!
It looks like this change of removing thread__ for fc_cache and fi_cache did not get compiled for me as I was simultaneously working on the testcode. Now I recompiled all and tried this once more and BANG! The problem was gone from my own program. And I also got it back by adding the thread__ ahead of those two caches. I think this solved the issue!
I must fly now... Will be back for more testing tomorrow.
A million thanks and best regards,
Tom
Good morning Mirek,
Removing "thread__" from "thread__ CharEntry fc_cache[512];" fixes it.
Removing "thread__" from "thread__ FontEntry fi_cache[63];" does not have any effect on the problem.
Best regards,
Tom
BTW: Why should the caches be thread_local anyway? Isn't it true that Painter will use the same fonts with a whole bunch of threads when painting in MT? Having them common for all threads would have the used fonts cached after first hit and available to all threads. (Well, I'm likely missing something important here, but you'll probably educate me on that shortly...)