1
// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
3
* Copyright 2011 Canonical Ltd.
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.
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.
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/>
19
* Authored by: Andrea Cimitan <andrea.cimitan@canonical.com>
20
* Nick Dedekind <nick.dedekind@canonical.com>
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"
41
nux::logging::Logger logger("unity.dash.previews.coverart");
43
const int icon_width = 256;
46
NUX_IMPLEMENT_OBJECT_TYPE(CoverArt);
49
: View(NUX_TRACKER_LOCATION)
50
, overlay_text_(nullptr)
53
, stretch_image_(false)
63
overlay_text_->UnReference();
67
IconLoader::GetDefault().DisconnectHandle(slot_handle_);
75
std::string CoverArt::GetName() const
80
void CoverArt::AddProperties(GVariantBuilder* builder)
82
variant::BuilderWrapper(builder)
83
.add(GetAbsoluteGeometry())
84
.add("image-hint", image_hint_)
85
.add("waiting", waiting_);
88
void CoverArt::SetImage(std::string const& image_hint)
90
spinner_timeout_.reset();
91
frame_timeout_.reset();
96
IconLoader::GetDefault().DisconnectHandle(slot_handle_);
100
GIcon* icon = g_icon_new_for_string(image_hint.c_str(), NULL);
102
bool bLoadTexture = false;
103
bLoadTexture |= g_strrstr(image_hint.c_str(), "://") != NULL;
104
if (!bLoadTexture && !image_hint.empty())
106
bLoadTexture |= image_hint[0] == '/' && image_hint.size() > 1;
109
// texture from file.
113
slot_handle_ = IconLoader::GetDefault().LoadFromGIconString(image_hint, ~0, sigc::mem_fun(this, &CoverArt::TextureLoaded));
115
else if (!image_hint.empty())
118
GetLayout()->RemoveChildObject(overlay_text_);
123
slot_handle_ = IconLoader::GetDefault().LoadFromGIconString(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded));
128
slot_handle_ = IconLoader::GetDefault().LoadFromIconName(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded));
133
SetNoImageAvailable();
137
g_object_unref(icon);
140
void CoverArt::GenerateImage(std::string const& uri)
142
notifier_ = ThumbnailGenerator::Instance().GetThumbnail(uri, 512);
146
notifier_->ready.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailGenerated));
147
notifier_->error.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailError));
152
SetNoImageAvailable();
156
void CoverArt::StartWaiting()
163
rotate_matrix_.Rotate_z(0.0f);
166
spinner_timeout_.reset(new glib::TimeoutSeconds(5, [&]
170
texture_screenshot_.Release();
171
SetNoImageAvailable();
178
void CoverArt::StopWaiting()
180
spinner_timeout_.reset();
181
frame_timeout_.reset();
185
void CoverArt::SetNoImageAvailable()
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();
197
void CoverArt::IconLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf)
201
stretch_image_ = false;
205
SetNoImageAvailable();
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))
216
pixbuf_width = (pixbuf_width) ? pixbuf_width : 1; // no zeros please
217
pixbuf_height = (pixbuf_height) ? pixbuf_height: 1; // no zeros please
220
if (pixbuf_width == pixbuf_height)
222
// quick path for square icons
223
texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
228
// slow path for non square icons that must be resized to fit in the square
231
float aspect = static_cast<float>(pixbuf_height) / pixbuf_width; // already sanitized width/height so can not be 0.0
234
pixbuf_width = icon_width;
235
pixbuf_height = pixbuf_width * aspect;
237
if (pixbuf_height > height)
239
// scaled too big, scale down
240
pixbuf_height = height;
241
pixbuf_width = pixbuf_height / aspect;
246
pixbuf_height = height;
247
pixbuf_width = pixbuf_height / aspect;
250
if (gdk_pixbuf_get_height(pixbuf) == pixbuf_height)
252
// we changed our mind, fast path is good
253
texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
258
nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, pixbuf_width, pixbuf_height);
259
cairo_t* cr = cairo_graphics.GetInternalContext();
261
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
264
float scale = float(pixbuf_height) / gdk_pixbuf_get_height(pixbuf);
265
cairo_scale(cr, scale, scale);
267
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
268
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
271
texture_screenshot_.Adopt(texture_from_cairo_graphics(cairo_graphics));
276
void CoverArt::TextureLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf)
280
stretch_image_ = true;
284
SetNoImageAvailable();
287
texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true));
290
void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw)
292
nux::Geometry const& base = GetGeometry();
294
gfx_engine.PushClippingRectangle(base);
295
nux::GetPainter().PaintBackground(gfx_engine, base);
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);
301
gfx_engine.QRP_Color(base.x,
305
nux::Color(0.03f, 0.03f, 0.03f, 0.0f));
307
if (texture_screenshot_)
309
nux::Geometry imageDest = base;
310
nux::TexCoordXForm texxform;
312
if (stretch_image_ || base.GetWidth() < texture_screenshot_->GetWidth() || base.height < texture_screenshot_->GetHeight())
314
float base_apsect = float(base.GetWidth()) / base.GetHeight();
315
float image_aspect = float(texture_screenshot_->GetWidth()) / texture_screenshot_->GetHeight();
317
if (image_aspect > base_apsect)
319
imageDest.SetHeight(float(imageDest.GetWidth()) / image_aspect);
321
if (image_aspect < base_apsect)
323
imageDest.SetWidth(image_aspect * imageDest.GetHeight());
328
imageDest = nux::Geometry(0, 0, texture_screenshot_->GetWidth(), texture_screenshot_->GetHeight());
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);
338
texxform.u1 = imageDest.width;
339
texxform.v1 = imageDest.height;
341
gfx_engine.QRP_1Tex(base.x + (float(base.GetWidth() - imageDest.GetWidth()) / 2),
342
base.y + (float(base.GetHeight() - imageDest.GetHeight()) / 2),
345
texture_screenshot_.GetPointer()->GetDeviceTexture(),
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;
359
nux::Geometry spin_geo(base.x + ((base.width - spin_->GetWidth()) / 2),
360
base.y + ((base.height - spin_->GetHeight()) / 2),
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;
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));
375
gfx_engine.QRP_1Tex(spin_geo.x,
379
spin_->GetDeviceTexture(),
383
gfx_engine.PopModelViewMatrix();
384
gfx_engine.PopModelViewMatrix();
385
gfx_engine.PopModelViewMatrix();
389
frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &CoverArt::OnFrameTimeout)));
394
gfx_engine.GetRenderStates().SetBlend(alpha, src, dest);
396
gfx_engine.PopClippingRectangle();
399
void CoverArt::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw)
401
nux::Geometry const& base = GetGeometry();
402
gfx_engine.PushClippingRectangle(base);
405
GetLayout()->ProcessDraw(gfx_engine, force_draw);
407
gfx_engine.PopClippingRectangle();
410
void CoverArt::SetupViews()
412
nux::VLayout* layout = new nux::VLayout();
413
layout->AddSpace(0, 1);
414
layout->AddSpace(0, 1);
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");
424
dash::Style& style = dash::Style::Instance();
425
spin_ = style.GetSearchSpinIcon();
427
rotate_matrix_.Identity();
428
rotate_matrix_.Rotate_z(0.0);
432
void CoverArt::SetFont(std::string const& font)
434
overlay_text_->SetFont(font);
437
void CoverArt::OnThumbnailGenerated(std::string const& uri)
443
void CoverArt::OnThumbnailError(std::string const& error_hint)
445
LOG_WARNING(logger) << "Failed to generate thumbnail: " << error_hint;
448
texture_screenshot_.Release();
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();
460
bool CoverArt::OnFrameTimeout()
464
if (rotation_ >= 360.0f)
467
rotate_matrix_.Rotate_z(rotation_);
470
frame_timeout_.reset();
b'\\ No newline at end of file'