Merge lp:~nick-dedekind/unity/lp1066788.cairo-text-stitching into lp:unity

Proposed by Nick Dedekind
Status: Merged
Approved by: Nick Dedekind
Approved revision: no longer in the source branch.
Merged at revision: 2953
Proposed branch: lp:~nick-dedekind/unity/lp1066788.cairo-text-stitching
Merge into: lp:unity
Diff against target: 469 lines (+231/-35)
10 files modified
dash/previews/StandaloneApplicationPreview.cpp (+4/-3)
tests/CMakeLists.txt (+1/-0)
tests/test_previews_application.cpp (+1/-1)
tests/test_previews_generic.cpp (+1/-1)
tests/test_previews_movie.cpp (+1/-1)
tests/test_previews_music.cpp (+1/-1)
tests/test_previews_social.cpp (+1/-2)
tests/test_static_cairo_text.cpp (+73/-0)
unity-shared/StaticCairoText.cpp (+145/-26)
unity-shared/StaticCairoText.h (+3/-0)
To merge this branch: bzr merge lp:~nick-dedekind/unity/lp1066788.cairo-text-stitching
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Brandon Schaefer (community) Approve
Stephen M. Webb (community) Needs Fixing
Review via email: mp+131365@code.launchpad.net

Commit message

Split StaticCairoText text into multiple cached textures when the height of the required texture is over GpuInfo::MaxTextureSize. (LP: #1066788)

Description of the change

= Problem description =

https://bugs.launchpad.net/unity/+bug/1066788
Text labels with greater texture height than capable in OpenGL will not render correctly.

= The fix =

Split StaticCairoText text into multiple cached textures when the height of the required texture is over GpuInfo::MaxTextureSize.

= Test coverage =

Added unit test for StaticCairoText to test splitting of texture.
test-gtest --gtest_filter=TestStaticCairoText.TextTextureSize

To post a comment you must log in.
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Hmm code looks good, and confirmed fixes the crash for me...but there is no test :(

Possibly add a manual test that says to open the StandaloneApplicationPreivew...(but they would have to compile...)

Or possibly a unit test that would attempt to make a texture that is to large and confirm that it is not returning NULL.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Hey Brandon, Nick,

as discussed during the week pre-UDS, no more manual tests permitted as we will have automated daily upload to ubuntu, and so, no way to have manual perf performed before a release. Please instrument this part of code so that we can have an unit test.

Revision history for this message
Stephen M. Webb (bregma) wrote :

Needs test.

review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

Added test

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Looks good to me.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

removed gcc-4.7 code and merged with trunk to resolve conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

fixed conflict merging with trunk.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dash/previews/StandaloneApplicationPreview.cpp'
2--- dash/previews/StandaloneApplicationPreview.cpp 2012-09-13 11:43:46 +0000
3+++ dash/previews/StandaloneApplicationPreview.cpp 2012-12-04 11:32:23 +0000
4@@ -156,8 +156,9 @@
5 app_name << "Skype";
6
7 const char* subtitle = "Version 3.2, Size 32 MB";
8- const char* description = "Skype is a proprietary voice-over-Internet Protocol service and software application originally created by Niklas Zennström and Janus Friis in 2003, and owned by Microsoft since 2011. \
9-The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system.";
10+ std::stringstream description;
11+ for (int i = 0; i < 700; i++)
12+ description << "Application description " << i << std::endl;
13
14 // creates a generic preview object
15 glib::Object<GIcon> iconHint1(g_icon_new_for_string("/usr/share/unity/5/lens-nav-music.svg", NULL));
16@@ -179,7 +180,7 @@
17 unity_protocol_preview_set_image_source_uri(proto_obj, "file:///home/nick/Skype.png");
18 unity_protocol_preview_set_title(proto_obj, app_name.str().c_str());
19 unity_protocol_preview_set_subtitle(proto_obj, subtitle);
20- unity_protocol_preview_set_description(proto_obj, description);
21+ unity_protocol_preview_set_description(proto_obj, description.str().c_str());
22 unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0);
23 unity_protocol_preview_add_action_with_hints(proto_obj, "launch", "Download", iconHint2, 0, action_hints1);
24 unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec"));
25
26=== modified file 'tests/CMakeLists.txt'
27--- tests/CMakeLists.txt 2012-12-03 15:31:29 +0000
28+++ tests/CMakeLists.txt 2012-12-04 11:32:23 +0000
29@@ -246,6 +246,7 @@
30 test_single_monitor_launcher_icon.cpp
31 test_expo_launcher_icon.cpp
32 test_showdesktop_handler.cpp
33+ test_static_cairo_text.cpp
34 test_switcher_controller.cpp
35 test_switcher_model.cpp
36 test_texture_cache.cpp
37
38=== modified file 'tests/test_previews_application.cpp'
39--- tests/test_previews_application.cpp 2012-09-13 16:11:54 +0000
40+++ tests/test_previews_application.cpp 2012-12-04 11:32:23 +0000
41@@ -14,7 +14,7 @@
42 * You should have received a copy of the GNU General Public License
43 * along with this program. If not, see <http://www.gnu.org/licenses/>.
44 *
45- * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>
46+ * Authored by: Nick Dedekind <nick.dedekinc@canonical.com>
47 */
48
49 #include <list>
50
51=== modified file 'tests/test_previews_generic.cpp'
52--- tests/test_previews_generic.cpp 2012-09-13 16:11:54 +0000
53+++ tests/test_previews_generic.cpp 2012-12-04 11:32:23 +0000
54@@ -14,7 +14,7 @@
55 * You should have received a copy of the GNU General Public License
56 * along with this program. If not, see <http://www.gnu.org/licenses/>.
57 *
58- * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>
59+ * Authored by: Nick Dedekind <nick.dedekinc@canonical.com>
60 */
61
62 #include <list>
63
64=== modified file 'tests/test_previews_movie.cpp'
65--- tests/test_previews_movie.cpp 2012-09-13 16:11:54 +0000
66+++ tests/test_previews_movie.cpp 2012-12-04 11:32:23 +0000
67@@ -14,7 +14,7 @@
68 * You should have received a copy of the GNU General Public License
69 * along with this program. If not, see <http://www.gnu.org/licenses/>.
70 *
71- * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>
72+ * Authored by: Nick Dedekind <nick.dedekinc@canonical.com>
73 */
74
75 #include <list>
76
77=== modified file 'tests/test_previews_music.cpp'
78--- tests/test_previews_music.cpp 2012-09-13 16:11:54 +0000
79+++ tests/test_previews_music.cpp 2012-12-04 11:32:23 +0000
80@@ -14,7 +14,7 @@
81 * You should have received a copy of the GNU General Public License
82 * along with this program. If not, see <http://www.gnu.org/licenses/>.
83 *
84- * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>
85+ * Authored by: Nick Dedekind <nick.dedekinc@canonical.com>
86 */
87
88 #include <list>
89
90=== modified file 'tests/test_previews_social.cpp'
91--- tests/test_previews_social.cpp 2012-09-14 19:53:57 +0000
92+++ tests/test_previews_social.cpp 2012-12-04 11:32:23 +0000
93@@ -14,8 +14,7 @@
94 * You should have received a copy of the GNU General Public License
95 * along with this program. If not, see <http://www.gnu.org/licenses/>.
96 *
97- * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>
98- * Ken VanDine <ken.vandine@canonical.com>
99+ * Authored by: Ken VanDine <ken.vandine@canonical.com>
100 */
101
102 #include <list>
103
104=== added file 'tests/test_static_cairo_text.cpp'
105--- tests/test_static_cairo_text.cpp 1970-01-01 00:00:00 +0000
106+++ tests/test_static_cairo_text.cpp 2012-12-04 11:32:23 +0000
107@@ -0,0 +1,73 @@
108+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
109+/*
110+ * Copyright (C) 2012 Canonical Ltd
111+ *
112+ * This program is free software: you can redistribute it and/or modify
113+ * it under the terms of the GNU General Public License version 3 as
114+ * published by the Free Software Foundation.
115+ *
116+ * This program is distributed in the hope that it will be useful,
117+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
118+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
119+ * GNU General Public License for more details.
120+ *
121+ * You should have received a copy of the GNU General Public License
122+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
123+ *
124+ * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
125+ */
126+
127+#include <list>
128+#include <gmock/gmock.h>
129+using namespace testing;
130+
131+#include <Nux/Nux.h>
132+#include <unity-shared/StaticCairoText.h>
133+#include "test_utils.h"
134+
135+using namespace unity;
136+
137+namespace
138+{
139+
140+class MockStaticCairoText : public nux::StaticCairoText
141+{
142+public:
143+ MockStaticCairoText():StaticCairoText("") {}
144+
145+ using StaticCairoText::GetTextureStartIndices;
146+ using StaticCairoText::GetTextureEndIndices;
147+};
148+
149+TEST(TestStaticCairoText, TextTextureSize)
150+{
151+ // Test multi-texture stitching support.
152+
153+ nux::ObjectPtr<MockStaticCairoText> text(new MockStaticCairoText());
154+ text->SetLines(-2000);
155+ text->SetMaximumWidth(100);
156+
157+ std::stringstream ss;
158+ std::vector<unsigned> starts;
159+ std::vector<unsigned> ends;
160+ while(starts.size() < 3)
161+ {
162+ for (int i = 0; i < 100; i++)
163+ ss << "Test string\n";
164+ text->SetText(ss.str());
165+
166+ starts = text->GetTextureStartIndices();
167+ ends = text->GetTextureEndIndices();
168+ ASSERT_TRUE(starts.size() == ends.size());
169+
170+ for (unsigned int start_index = 0; start_index < starts.size(); start_index++)
171+ {
172+ if (start_index > 0)
173+ {
174+ ASSERT_EQ(starts[start_index], ends[start_index-1]+1);
175+ }
176+ }
177+ }
178+}
179+
180+}
181
182=== modified file 'unity-shared/StaticCairoText.cpp'
183--- unity-shared/StaticCairoText.cpp 2012-11-23 16:07:12 +0000
184+++ unity-shared/StaticCairoText.cpp 2012-12-04 11:32:23 +0000
185@@ -17,6 +17,7 @@
186 * Authored by: Jay Taoko <jay.taoko@canonical.com>
187 * Mirco Müller <mirco.mueller@canonical.com>
188 * Tim Penhey <tim.penhey@canonical.com>
189+ * Nick Dedekind <nick.dedekind@canonical.com>
190 */
191
192 #include "StaticCairoText.h"
193@@ -52,10 +53,26 @@
194 PangoAlignment GetPangoAlignment() const;
195
196 std::string GetEffectiveFont() const;
197+
198+ struct CacheTexture
199+ {
200+ typedef std::shared_ptr<CacheTexture> Ptr;
201+ CacheTexture()
202+ : start_index(0)
203+ , length((unsigned)std::string::npos)
204+ , height(0)
205+ {}
206+
207+ unsigned start_index;
208+ unsigned length;
209+ unsigned height;
210+
211+ std::shared_ptr<CairoGraphics> cr;
212+ };
213 Size GetTextExtents() const;
214
215 void SetAttributes(PangoLayout* layout);
216- void DrawText(cairo_t* cr, int width, int height, int line_spacing, Color const& color);
217+ void DrawText(CacheTexture::Ptr cached_texture, int width, int height, int line_spacing, Color const& color);
218
219 void UpdateTexture();
220 void OnFontChanged();
221@@ -69,6 +86,7 @@
222 mutable Size cached_extent_;
223 mutable Size cached_base_;
224 mutable int baseline_;
225+ mutable std::list<CacheTexture::Ptr> cache_textures_;
226
227 std::string text_;
228 Color text_color_;
229@@ -80,7 +98,7 @@
230
231 std::string font_;
232
233- BaseTexturePtr texture2D_;
234+ std::list<BaseTexturePtr> textures2D_;
235
236 Size pre_layout_size_;
237
238@@ -217,7 +235,7 @@
239 SetBaseSize(pimpl->cached_extent_.width,
240 pimpl->cached_extent_.height);
241
242- if (pimpl->texture2D_.IsNull())
243+ if (pimpl->textures2D_.empty())
244 {
245 pimpl->UpdateTexture();
246 }
247@@ -254,7 +272,7 @@
248 {
249 Geometry const& base = GetGeometry();
250
251- if (pimpl->texture2D_.IsNull() ||
252+ if (pimpl->textures2D_.empty() ||
253 pimpl->cached_base_.width != base.width ||
254 pimpl->cached_base_.height != base.height)
255 {
256@@ -284,13 +302,24 @@
257 base.height,
258 col);
259
260- gfxContext.QRP_1Tex(base.x,
261- base.y + ((base.height - pimpl->cached_extent_.height) / 2),
262- base.width,
263- base.height,
264- pimpl->texture2D_->GetDeviceTexture(),
265- texxform,
266- pimpl->text_color_);
267+ int current_y = base.y + ((base.height - pimpl->cached_extent_.height) / 2);
268+
269+ for (BaseTexturePtr tex : pimpl->textures2D_)
270+ {
271+ nux::ObjectPtr<nux::IOpenGLBaseTexture> text_tex = tex->GetDeviceTexture();
272+ if (!text_tex)
273+ break;
274+
275+ gfxContext.QRP_1Tex(base.x,
276+ current_y,
277+ text_tex->GetWidth(),
278+ text_tex->GetHeight(),
279+ text_tex,
280+ texxform,
281+ pimpl->text_color_);
282+
283+ current_y += text_tex->GetHeight();
284+ }
285
286 gfxContext.GetRenderStates().SetBlend(alpha, src, dest);
287
288@@ -427,6 +456,44 @@
289 height = s.height;
290 }
291
292+std::vector<unsigned> StaticCairoText::GetTextureStartIndices()
293+{
294+ pimpl->GetTextExtents();
295+
296+ std::vector<unsigned> list;
297+ auto iter = pimpl->cache_textures_.begin();
298+ for (; iter != pimpl->cache_textures_.end(); ++iter)
299+ {
300+ Impl::CacheTexture::Ptr const& cached_texture = *iter;
301+ list.push_back(cached_texture->start_index);
302+ }
303+ return list;
304+}
305+
306+std::vector<unsigned> StaticCairoText::GetTextureEndIndices()
307+{
308+ pimpl->GetTextExtents();
309+
310+ std::vector<unsigned> list;
311+ auto iter = pimpl->cache_textures_.begin();
312+ for (; iter != pimpl->cache_textures_.end(); ++iter)
313+ {
314+ Impl::CacheTexture::Ptr const& cached_texture = *iter;
315+ if (cached_texture->length == (unsigned)std::string::npos)
316+ {
317+ list.push_back((unsigned)std::string::npos);
318+ }
319+ else
320+ {
321+ if (cached_texture->start_index > 0 || cached_texture->length > 0)
322+ list.push_back((unsigned)(cached_texture->start_index + cached_texture->length - 1));
323+ else
324+ list.push_back(0);
325+ }
326+ }
327+ return list;
328+}
329+
330 std::string StaticCairoText::Impl::GetEffectiveFont() const
331 {
332 if (font_.empty())
333@@ -506,6 +573,50 @@
334 baseline_ = pango_layout_get_baseline(layout) / PANGO_SCALE;
335 need_new_extent_cache_ = false;
336
337+ cache_textures_.clear();
338+ PangoLayoutIter* iter = pango_layout_get_iter(layout);
339+ CacheTexture::Ptr current_tex(new CacheTexture());
340+ const int max_height = GetGraphicsDisplay()->GetGpuDevice()->GetGpuInfo().GetMaxTextureSize();
341+ if (max_height < 0)
342+ return nux::Size(0, 0);
343+
344+ do
345+ {
346+ PangoLayoutLine* line = pango_layout_iter_get_line_readonly(iter);
347+ int y0 = 0, y1 = 0;
348+ pango_layout_iter_get_line_yrange(iter, &y0, &y1);
349+ y0 /= PANGO_SCALE;
350+ y1 /= PANGO_SCALE;
351+
352+ if (line->start_index < 0 || y1 < y0)
353+ {
354+ current_tex.reset();
355+ break;
356+ }
357+ unsigned line_start_index = line->start_index;
358+ unsigned height = y1-y0;
359+
360+ if (current_tex->height + height > (unsigned)max_height)
361+ {
362+ if (line_start_index > current_tex->start_index)
363+ current_tex->length = line_start_index - current_tex->start_index;
364+ else
365+ current_tex->length = 0;
366+ cache_textures_.push_back(current_tex);
367+
368+ // new texture.
369+ current_tex.reset(new CacheTexture());
370+ current_tex->start_index = line_start_index;
371+ current_tex->height = 0;
372+ }
373+ current_tex->height += height;
374+ }
375+ while(pango_layout_iter_next_line(iter));
376+
377+ if (current_tex) { cache_textures_.push_back(current_tex); }
378+
379+ pango_layout_iter_free(iter);
380+
381 // clean up
382 pango_font_description_free(desc);
383 g_object_unref(layout);
384@@ -545,12 +656,17 @@
385 pango_layout_set_attributes(layout, attr_list);
386 }
387
388-void StaticCairoText::Impl::DrawText(cairo_t* cr,
389+void StaticCairoText::Impl::DrawText(CacheTexture::Ptr cached_texture,
390 int width,
391 int height,
392 int line_spacing,
393 Color const& color)
394 {
395+ if (!cached_texture)
396+ return;
397+ cached_texture->cr.reset(new CairoGraphics(CAIRO_FORMAT_ARGB32, width, height));
398+ cairo_t* cr = cached_texture->cr->GetInternalContext();
399+
400 PangoLayout* layout = NULL;
401 PangoFontDescription* desc = NULL;
402 PangoContext* pangoCtx = NULL;
403@@ -558,18 +674,19 @@
404 GdkScreen* screen = gdk_screen_get_default(); // not ref'ed
405 GtkSettings* settings = gtk_settings_get_default(); // not ref'ed
406
407+ std::string text = text_.substr(cached_texture->start_index, cached_texture->length);
408+
409 std::string font(GetEffectiveFont());
410-
411 cairo_set_font_options(cr, gdk_screen_get_font_options(screen));
412+
413 layout = pango_cairo_create_layout(cr);
414-
415-
416 desc = pango_font_description_from_string(font.c_str());
417+
418 pango_layout_set_font_description(layout, desc);
419 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
420 pango_layout_set_ellipsize(layout, GetPangoEllipsizeMode());
421 pango_layout_set_alignment(layout, GetPangoAlignment());
422- pango_layout_set_markup(layout, text_.c_str(), -1);
423+ pango_layout_set_markup(layout, text.c_str(), -1);
424 pango_layout_set_width(layout, width * PANGO_SCALE);
425 pango_layout_set_height(layout, height * PANGO_SCALE);
426 pango_layout_set_spacing(layout, line_spacing * PANGO_SCALE);
427@@ -615,16 +732,18 @@
428 {
429 Size size = GetTextExtents();
430 parent_->SetBaseSize(size.width, size.height);
431- // Now reget the internal geometry as it is clipped by the max size.
432- Geometry const& geo = parent_->GetGeometry();
433-
434- CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32,
435- geo.width, geo.height);
436-
437- DrawText(cairo_graphics.GetInternalContext(),
438- geo.width, geo.height, line_spacing_, text_color_);
439-
440- texture2D_ = texture_ptr_from_cairo_graphics(cairo_graphics);
441+ nux::Geometry const& geo = parent_->GetGeometry();
442+
443+ textures2D_.clear();
444+ auto iter = cache_textures_.begin();
445+ for (; iter != cache_textures_.end(); ++iter)
446+ {
447+ CacheTexture::Ptr const& cached_texture = *iter;
448+ DrawText(cached_texture,
449+ geo.width, cached_texture->height, line_spacing_, text_color_);
450+
451+ textures2D_.push_back(texture_ptr_from_cairo_graphics(*cached_texture->cr));
452+ }
453 }
454
455 void StaticCairoText::Impl::FontChanged(GObject* gobject,
456
457=== modified file 'unity-shared/StaticCairoText.h'
458--- unity-shared/StaticCairoText.h 2012-11-23 16:07:12 +0000
459+++ unity-shared/StaticCairoText.h 2012-12-04 11:32:23 +0000
460@@ -110,6 +110,9 @@
461 // Key navigation
462 virtual bool AcceptKeyNavFocus();
463
464+ std::vector<unsigned> GetTextureStartIndices();
465+ std::vector<unsigned> GetTextureEndIndices();
466+
467 private:
468 struct Impl;
469 Impl* pimpl;