~azzar1/unity/fix-1028810

« back to all changes in this revision

Viewing changes to unity-shared/CoverArt.cpp

  • Committer: Andrea Azzarone
  • Date: 2012-08-22 13:14:18 UTC
  • mfrom: (2516.1.92 unity)
  • Revision ID: azzaronea@gmail.com-20120822131418-mrfwx82k39xnvl9e
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
 
2
/*
 
3
 * Copyright 2011 Canonical Ltd.
 
4
 *
 
5
 * This program is free software: you can redistribute it and/or modify it
 
6
 * under the terms of the GNU Lesser General Public License version 3, as
 
7
 * published by the  Free Software Foundation.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful, but
 
10
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 
11
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 
12
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
 
13
 * License for more details.
 
14
 *
 
15
 * You should have received a copy of both the GNU Lesser General Public
 
16
 * License version 3 along with this program.  If not, see
 
17
 * <http://www.gnu.org/licenses/>
 
18
 *
 
19
 * Authored by: Andrea Cimitan <andrea.cimitan@canonical.com>
 
20
 *              Nick Dedekind <nick.dedekind@canonical.com>
 
21
 *
 
22
 */
 
23
 
 
24
#include "CoverArt.h"
 
25
#include "unity-shared/IntrospectableWrappers.h"
 
26
#include "unity-shared/CairoTexture.h"
 
27
#include <NuxCore/Logger.h>
 
28
#include <Nux/VLayout.h>
 
29
#include "DashStyle.h"
 
30
#include "IconLoader.h"
 
31
 
 
32
namespace unity
 
33
{
 
34
namespace dash
 
35
{
 
36
namespace previews
 
37
{
 
38
 
 
39
namespace
 
40
{
 
41
nux::logging::Logger logger("unity.dash.previews.coverart");
 
42
 
 
43
const int icon_width = 256;
 
44
}
 
45
 
 
46
NUX_IMPLEMENT_OBJECT_TYPE(CoverArt);
 
47
 
 
48
CoverArt::CoverArt()
 
49
  : View(NUX_TRACKER_LOCATION)
 
50
  , overlay_text_(nullptr)
 
51
  , thumb_handle_(0)
 
52
  , slot_handle_(0)
 
53
  , stretch_image_(false)
 
54
  , waiting_(false)
 
55
  , rotation_(0.0)
 
56
{
 
57
  SetupViews();
 
58
}
 
59
 
 
60
CoverArt::~CoverArt()
 
61
{
 
62
  if (overlay_text_)
 
63
    overlay_text_->UnReference();
 
64
 
 
65
  if (slot_handle_ > 0)
 
66
  {
 
67
    IconLoader::GetDefault().DisconnectHandle(slot_handle_);
 
68
    slot_handle_ = 0;
 
69
  }
 
70
 
 
71
  if (notifier_)
 
72
    notifier_->Cancel();
 
73
}
 
74
 
 
75
std::string CoverArt::GetName() const
 
76
{
 
77
  return "CoverArt";
 
78
}
 
79
 
 
80
void CoverArt::AddProperties(GVariantBuilder* builder)
 
81
{
 
82
  variant::BuilderWrapper(builder)
 
83
    .add(GetAbsoluteGeometry())
 
84
    .add("image-hint", image_hint_)
 
85
    .add("waiting", waiting_);
 
86
}
 
87
 
 
88
void CoverArt::SetImage(std::string const& image_hint)
 
89
 
90
  spinner_timeout_.reset();
 
91
  frame_timeout_.reset();
 
92
  waiting_ = false;
 
93
 
 
94
  if (slot_handle_ > 0)
 
95
  {
 
96
    IconLoader::GetDefault().DisconnectHandle(slot_handle_);
 
97
    slot_handle_ = 0;
 
98
  }
 
99
 
 
100
  GIcon* icon = g_icon_new_for_string(image_hint.c_str(), NULL);
 
101
 
 
102
  bool bLoadTexture = false;
 
103
  bLoadTexture |= g_strrstr(image_hint.c_str(), "://") != NULL;
 
104
  if (!bLoadTexture && !image_hint.empty())
 
105
  {
 
106
    bLoadTexture |= image_hint[0] == '/' && image_hint.size() > 1;
 
107
  }
 
108
 
 
109
  // texture from file.
 
110
  if (bLoadTexture)
 
111
  {    
 
112
    StartWaiting();
 
113
    slot_handle_ = IconLoader::GetDefault().LoadFromGIconString(image_hint, ~0, sigc::mem_fun(this, &CoverArt::TextureLoaded));
 
114
  }
 
115
  else if (!image_hint.empty())
 
116
  {
 
117
    if (GetLayout())
 
118
      GetLayout()->RemoveChildObject(overlay_text_);
 
119
    
 
120
    if (G_IS_ICON(icon))
 
121
    {
 
122
      StartWaiting();
 
123
      slot_handle_ = IconLoader::GetDefault().LoadFromGIconString(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded));
 
124
    }
 
125
    else
 
126
    {
 
127
      StartWaiting();
 
128
      slot_handle_ = IconLoader::GetDefault().LoadFromIconName(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded));
 
129
    }
 
130
  }
 
131
  else
 
132
  {
 
133
    SetNoImageAvailable();
 
134
  }
 
135
 
 
136
  if (icon != NULL)
 
137
    g_object_unref(icon);
 
138
}
 
139
 
 
140
void CoverArt::GenerateImage(std::string const& uri)
 
141
{
 
142
  notifier_ = ThumbnailGenerator::Instance().GetThumbnail(uri, 512);
 
143
  if (notifier_)
 
144
  {
 
145
    StartWaiting();
 
146
    notifier_->ready.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailGenerated));
 
147
    notifier_->error.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailError));
 
148
  }
 
149
  else
 
150
  {
 
151
    StopWaiting();
 
152
    SetNoImageAvailable();    
 
153
  }
 
154
}
 
155
 
 
156
void CoverArt::StartWaiting()
 
157
{
 
158
  if (waiting_)
 
159
    return;
 
160
 
 
161
  waiting_ = true;
 
162
 
 
163
  rotate_matrix_.Rotate_z(0.0f);
 
164
  rotation_ = 0.0f;
 
165
 
 
166
  spinner_timeout_.reset(new glib::TimeoutSeconds(5, [&]
 
167
  {
 
168
    StopWaiting();
 
169
 
 
170
    texture_screenshot_.Release();
 
171
    SetNoImageAvailable();
 
172
    return false;
 
173
  }));
 
174
  
 
175
  QueueDraw();
 
176
}
 
177
 
 
178
void CoverArt::StopWaiting()
 
179
{
 
180
  spinner_timeout_.reset();
 
181
  frame_timeout_.reset();
 
182
  waiting_ = false;
 
183
}
 
184
 
 
185
void CoverArt::SetNoImageAvailable()
 
186
{
 
187
  if (GetLayout())
 
188
  {
 
189
    GetLayout()->RemoveChildObject(overlay_text_);
 
190
    GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1));
 
191
    ComputeContentSize();
 
192
    
 
193
    QueueDraw();
 
194
  }
 
195
}
 
196
 
 
197
void CoverArt::IconLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf)
 
198
{
 
199
  // Finished waiting
 
200
  StopWaiting();
 
201
  stretch_image_ = false;
 
202
 
 
203
  if (!pixbuf)
 
204
  {
 
205
    SetNoImageAvailable();
 
206
    return;
 
207
  }
 
208
 
 
209
  int height = size;
 
210
 
 
211
  int pixbuf_width, pixbuf_height;
 
212
  pixbuf_width = gdk_pixbuf_get_width(pixbuf);
 
213
  pixbuf_height = gdk_pixbuf_get_height(pixbuf);
 
214
  if (G_UNLIKELY(!pixbuf_height || !pixbuf_width))
 
215
  {
 
216
    pixbuf_width = (pixbuf_width) ? pixbuf_width : 1; // no zeros please
 
217
    pixbuf_height = (pixbuf_height) ? pixbuf_height: 1; // no zeros please
 
218
  }
 
219
 
 
220
  if (pixbuf_width == pixbuf_height)
 
221
  {
 
222
    // quick path for square icons
 
223
    texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
 
224
    QueueDraw();
 
225
  }
 
226
  else
 
227
  {
 
228
    // slow path for non square icons that must be resized to fit in the square
 
229
    // texture
 
230
 
 
231
    float aspect = static_cast<float>(pixbuf_height) / pixbuf_width; // already sanitized width/height so can not be 0.0
 
232
    if (aspect < 1.0f)
 
233
    {
 
234
      pixbuf_width = icon_width;
 
235
      pixbuf_height = pixbuf_width * aspect;
 
236
 
 
237
      if (pixbuf_height > height)
 
238
      {
 
239
        // scaled too big, scale down
 
240
        pixbuf_height = height;
 
241
        pixbuf_width = pixbuf_height / aspect;
 
242
      }
 
243
    }
 
244
    else
 
245
    {
 
246
      pixbuf_height = height;
 
247
      pixbuf_width = pixbuf_height / aspect;
 
248
    }
 
249
 
 
250
    if (gdk_pixbuf_get_height(pixbuf) == pixbuf_height)
 
251
    {
 
252
      // we changed our mind, fast path is good
 
253
      texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
 
254
      QueueDraw();
 
255
      return;
 
256
    }
 
257
 
 
258
    nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, pixbuf_width, pixbuf_height);
 
259
    cairo_t* cr = cairo_graphics.GetInternalContext();
 
260
 
 
261
    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
 
262
    cairo_paint(cr);
 
263
 
 
264
    float scale = float(pixbuf_height) / gdk_pixbuf_get_height(pixbuf);
 
265
    cairo_scale(cr, scale, scale);
 
266
 
 
267
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
 
268
    gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
 
269
    cairo_paint(cr);
 
270
 
 
271
    texture_screenshot_.Adopt(texture_from_cairo_graphics(cairo_graphics));
 
272
    QueueDraw();
 
273
  }
 
274
}
 
275
 
 
276
void CoverArt::TextureLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf)
 
277
{
 
278
  // Finished waiting
 
279
  StopWaiting();
 
280
  stretch_image_ = true;
 
281
 
 
282
  if (!pixbuf)
 
283
  {
 
284
    SetNoImageAvailable();
 
285
    return;
 
286
  }
 
287
  texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
 
288
}
 
289
 
 
290
void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw)
 
291
{
 
292
  nux::Geometry const& base = GetGeometry();
 
293
 
 
294
  gfx_engine.PushClippingRectangle(base);
 
295
  nux::GetPainter().PaintBackground(gfx_engine, base);
 
296
 
 
297
  unsigned int alpha, src, dest = 0;
 
298
  gfx_engine.GetRenderStates().GetBlend(alpha, src, dest);
 
299
  gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
300
 
 
301
  gfx_engine.QRP_Color(base.x,
 
302
                    base.y,
 
303
                    base.GetWidth(),
 
304
                    base.GetHeight(),
 
305
                    nux::Color(0.03f, 0.03f, 0.03f, 0.0f));
 
306
 
 
307
  if (texture_screenshot_)
 
308
  {
 
309
    nux::Geometry imageDest = base;
 
310
    nux::TexCoordXForm texxform;
 
311
 
 
312
    if (stretch_image_ || base.GetWidth() < texture_screenshot_->GetWidth() || base.height < texture_screenshot_->GetHeight())
 
313
    {
 
314
      float base_apsect = float(base.GetWidth()) / base.GetHeight();
 
315
      float image_aspect = float(texture_screenshot_->GetWidth()) / texture_screenshot_->GetHeight();
 
316
 
 
317
      if (image_aspect > base_apsect)
 
318
      {
 
319
        imageDest.SetHeight(float(imageDest.GetWidth()) / image_aspect);
 
320
      } 
 
321
      if (image_aspect < base_apsect)
 
322
      {
 
323
        imageDest.SetWidth(image_aspect * imageDest.GetHeight());
 
324
      }
 
325
    }
 
326
    else
 
327
    {
 
328
      imageDest = nux::Geometry(0, 0, texture_screenshot_->GetWidth(), texture_screenshot_->GetHeight());
 
329
    }
 
330
 
 
331
 
 
332
    texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_SCALE_COORD);
 
333
    texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER);
 
334
    texxform.SetFilter(nux::TEXFILTER_LINEAR, nux::TEXFILTER_LINEAR);
 
335
 
 
336
    texxform.u0 = 0;
 
337
    texxform.v0 = 0;
 
338
    texxform.u1 = imageDest.width;
 
339
    texxform.v1 = imageDest.height;
 
340
 
 
341
    gfx_engine.QRP_1Tex(base.x + (float(base.GetWidth() - imageDest.GetWidth()) / 2),
 
342
                        base.y + (float(base.GetHeight() - imageDest.GetHeight()) / 2),
 
343
                        imageDest.width,
 
344
                        imageDest.height,
 
345
                        texture_screenshot_.GetPointer()->GetDeviceTexture(),
 
346
                        texxform,
 
347
                        nux::color::White);
 
348
  }
 
349
  else
 
350
  {
 
351
    if (waiting_)
 
352
    {  
 
353
      nux::TexCoordXForm texxform;
 
354
      texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
 
355
      texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT);
 
356
      texxform.min_filter = nux::TEXFILTER_LINEAR;
 
357
      texxform.mag_filter = nux::TEXFILTER_LINEAR;
 
358
 
 
359
      nux::Geometry spin_geo(base.x + ((base.width - spin_->GetWidth()) / 2),
 
360
                             base.y + ((base.height - spin_->GetHeight()) / 2),
 
361
                             spin_->GetWidth(),
 
362
                             spin_->GetHeight());
 
363
      // Geometry (== Rect) uses integers which were rounded above,
 
364
      // hence an extra 0.5 offset for odd sizes is needed
 
365
      // because pure floating point is not being used.
 
366
      int spin_offset_w = !(base.width % 2) ? 0 : 1;
 
367
      int spin_offset_h = !(base.height % 2) ? 0 : 1;
 
368
 
 
369
      gfx_engine.PushModelViewMatrix(nux::Matrix4::TRANSLATE(-spin_geo.x - (spin_geo.width + spin_offset_w) / 2.0f,
 
370
                                                             -spin_geo.y - (spin_geo.height + spin_offset_h) / 2.0f, 0));
 
371
      gfx_engine.PushModelViewMatrix(rotate_matrix_);
 
372
      gfx_engine.PushModelViewMatrix(nux::Matrix4::TRANSLATE(spin_geo.x + (spin_geo.width + spin_offset_w) / 2.0f,
 
373
                                                             spin_geo.y + (spin_geo.height + spin_offset_h) / 2.0f, 0));
 
374
 
 
375
      gfx_engine.QRP_1Tex(spin_geo.x,
 
376
                          spin_geo.y,
 
377
                          spin_geo.width,
 
378
                          spin_geo.height,
 
379
                          spin_->GetDeviceTexture(),
 
380
                          texxform,
 
381
                          nux::color::White);
 
382
 
 
383
      gfx_engine.PopModelViewMatrix();
 
384
      gfx_engine.PopModelViewMatrix();
 
385
      gfx_engine.PopModelViewMatrix();
 
386
 
 
387
      if (!frame_timeout_)
 
388
      {
 
389
        frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &CoverArt::OnFrameTimeout)));
 
390
      }
 
391
    }
 
392
  }
 
393
  
 
394
  gfx_engine.GetRenderStates().SetBlend(alpha, src, dest);
 
395
 
 
396
  gfx_engine.PopClippingRectangle();
 
397
}
 
398
 
 
399
void CoverArt::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw)
 
400
{
 
401
  nux::Geometry const& base = GetGeometry();
 
402
  gfx_engine.PushClippingRectangle(base);
 
403
 
 
404
  if (GetLayout())
 
405
    GetLayout()->ProcessDraw(gfx_engine, force_draw);
 
406
 
 
407
  gfx_engine.PopClippingRectangle();
 
408
}
 
409
 
 
410
void CoverArt::SetupViews()
 
411
{
 
412
  nux::VLayout* layout = new nux::VLayout();
 
413
  layout->AddSpace(0, 1);
 
414
  layout->AddSpace(0, 1);
 
415
  SetLayout(layout);
 
416
 
 
417
  overlay_text_ = new nux::StaticCairoText("", NUX_TRACKER_LOCATION);
 
418
  overlay_text_->Reference();
 
419
  overlay_text_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_CENTRE);
 
420
  overlay_text_->SetFont("Ubuntu 14");
 
421
  overlay_text_->SetLines(-3);
 
422
  overlay_text_->SetText("No Image Available");
 
423
 
 
424
  dash::Style& style = dash::Style::Instance();
 
425
  spin_ = style.GetSearchSpinIcon();
 
426
 
 
427
  rotate_matrix_.Identity();
 
428
  rotate_matrix_.Rotate_z(0.0);
 
429
 
 
430
}
 
431
 
 
432
void CoverArt::SetFont(std::string const& font)
 
433
{
 
434
  overlay_text_->SetFont(font);
 
435
}
 
436
 
 
437
void CoverArt::OnThumbnailGenerated(std::string const& uri)
 
438
{
 
439
  SetImage(uri);
 
440
  notifier_.Release();
 
441
}
 
442
 
 
443
void CoverArt::OnThumbnailError(std::string const& error_hint)
 
444
{
 
445
  LOG_WARNING(logger) << "Failed to generate thumbnail: " << error_hint;
 
446
  StopWaiting();
 
447
 
 
448
  texture_screenshot_.Release();
 
449
  if (GetLayout())
 
450
  {
 
451
    GetLayout()->RemoveChildObject(overlay_text_);
 
452
    GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1));   
 
453
    ComputeContentSize();
 
454
  }
 
455
  QueueDraw();
 
456
  notifier_.Release();
 
457
}
 
458
 
 
459
 
 
460
bool CoverArt::OnFrameTimeout()
 
461
{
 
462
  rotation_ += 0.1f;
 
463
 
 
464
  if (rotation_ >= 360.0f)
 
465
    rotation_ = 0.0f;
 
466
 
 
467
  rotate_matrix_.Rotate_z(rotation_);
 
468
  QueueDraw();
 
469
 
 
470
  frame_timeout_.reset();
 
471
  return false;
 
472
}
 
473
 
 
474
}
 
475
}
 
476
}
 
 
b'\\ No newline at end of file'