Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / pictures / Picture.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22 #if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
23   #include "config.h"
24 #endif
25
26 #include "Picture.h"
27 #include "settings/AdvancedSettings.h"
28 #include "settings/Settings.h"
29 #include "FileItem.h"
30 #include "filesystem/File.h"
31 #include "utils/log.h"
32 #include "utils/URIUtils.h"
33 #include "DllSwScale.h"
34 #include "guilib/Texture.h"
35 #include "guilib/imagefactory.h"
36 #if defined(HAS_OMXPLAYER)
37 #include "cores/omxplayer/OMXImage.h"
38 #endif
39
40 using namespace XFILE;
41
42 bool CPicture::CreateThumbnailFromSurface(const unsigned char *buffer, int width, int height, int stride, const CStdString &thumbFile)
43 {
44   CLog::Log(LOGDEBUG, "cached image '%s' size %dx%d", thumbFile.c_str(), width, height);
45   if (URIUtils::HasExtension(thumbFile, ".jpg"))
46   {
47 #if defined(HAS_OMXPLAYER)
48     if (COMXImage::CreateThumbnailFromSurface((BYTE *)buffer, width, height, XB_FMT_A8R8G8B8, stride, thumbFile.c_str()))
49       return true;
50 #endif
51   }
52
53   unsigned char *thumb = NULL;
54   unsigned int thumbsize=0;
55   IImage* pImage = ImageFactory::CreateLoader(thumbFile);
56   if(pImage == NULL || !pImage->CreateThumbnailFromSurface((BYTE *)buffer, width, height, XB_FMT_A8R8G8B8, stride, thumbFile.c_str(), thumb, thumbsize))
57   {
58     CLog::Log(LOGERROR, "Failed to CreateThumbnailFromSurface for %s", thumbFile.c_str());
59     delete pImage;
60     return false;
61   }
62
63   XFILE::CFile file;
64   if (file.OpenForWrite(thumbFile, true))
65   {
66     file.Write(thumb, thumbsize);
67     file.Close();
68     pImage->ReleaseThumbnailBuffer();
69     delete pImage;
70     return true;
71   }
72   pImage->ReleaseThumbnailBuffer();
73   delete pImage;
74   return false;
75 }
76
77 CThumbnailWriter::CThumbnailWriter(unsigned char* buffer, int width, int height, int stride, const CStdString& thumbFile)
78 {
79   m_buffer    = buffer;
80   m_width     = width;
81   m_height    = height;
82   m_stride    = stride;
83   m_thumbFile = thumbFile;
84 }
85
86 bool CThumbnailWriter::DoWork()
87 {
88   bool success = true;
89
90   if (!CPicture::CreateThumbnailFromSurface(m_buffer, m_width, m_height, m_stride, m_thumbFile))
91   {
92     CLog::Log(LOGERROR, "CThumbnailWriter::DoWork unable to write %s", m_thumbFile.c_str());
93     success = false;
94   }
95
96   delete [] m_buffer;
97
98   return success;
99 }
100
101 bool CPicture::CacheTexture(CBaseTexture *texture, uint32_t &dest_width, uint32_t &dest_height, const std::string &dest)
102 {
103   return CacheTexture(texture->GetPixels(), texture->GetWidth(), texture->GetHeight(), texture->GetPitch(),
104                       texture->GetOrientation(), dest_width, dest_height, dest);
105 }
106
107 bool CPicture::CacheTexture(uint8_t *pixels, uint32_t width, uint32_t height, uint32_t pitch, int orientation, uint32_t &dest_width, uint32_t &dest_height, const std::string &dest)
108 {
109   // if no max width or height is specified, don't resize
110   if (dest_width == 0)
111     dest_width = width;
112   if (dest_height == 0)
113     dest_height = height;
114
115   uint32_t max_height = g_advancedSettings.m_imageRes;
116   if (g_advancedSettings.m_fanartRes > g_advancedSettings.m_imageRes)
117   { // 16x9 images larger than the fanart res use that rather than the image res
118     if (fabsf((float)width / (float)height / (16.0f/9.0f) - 1.0f) <= 0.01f && height >= g_advancedSettings.m_fanartRes)
119     {
120       max_height = g_advancedSettings.m_fanartRes;
121     }
122   }
123   uint32_t max_width = max_height * 16/9;
124
125   dest_height = std::min(dest_height, max_height);
126   dest_width  = std::min(dest_width, max_width);
127
128   if (width > dest_width || height > dest_height || orientation)
129   {
130     bool success = false;
131
132     dest_width = std::min(width, dest_width);
133     dest_height = std::min(height, dest_height);
134
135     // create a buffer large enough for the resulting image
136     GetScale(width, height, dest_width, dest_height);
137     uint32_t *buffer = new uint32_t[dest_width * dest_height];
138     if (buffer)
139     {
140       if (ScaleImage(pixels, width, height, pitch,
141                      (uint8_t *)buffer, dest_width, dest_height, dest_width * 4))
142       {
143         if (!orientation || OrientateImage(buffer, dest_width, dest_height, orientation))
144         {
145           success = CreateThumbnailFromSurface((unsigned char*)buffer, dest_width, dest_height, dest_width * 4, dest);
146         }
147       }
148       delete[] buffer;
149     }
150     return success;
151   }
152   else
153   { // no orientation needed
154     dest_width = width;
155     dest_height = height;
156     return CreateThumbnailFromSurface(pixels, width, height, pitch, dest);
157   }
158   return false;
159 }
160
161 bool CPicture::CreateTiledThumb(const std::vector<std::string> &files, const std::string &thumb)
162 {
163   if (!files.size())
164     return false;
165
166   unsigned int num_across = (unsigned int)ceil(sqrt((float)files.size()));
167   unsigned int num_down = (files.size() + num_across - 1) / num_across;
168
169   unsigned int tile_width = g_advancedSettings.GetThumbSize() / num_across;
170   unsigned int tile_height = g_advancedSettings.GetThumbSize() / num_down;
171   unsigned int tile_gap = 1;
172   bool success = false;
173
174   // create a buffer for the resulting thumb
175   uint32_t *buffer = (uint32_t *)calloc(g_advancedSettings.GetThumbSize() * g_advancedSettings.GetThumbSize(), 4);
176   for (unsigned int i = 0; i < files.size(); ++i)
177   {
178     int x = i % num_across;
179     int y = i / num_across;
180     // load in the image
181     unsigned int width = tile_width - 2*tile_gap, height = tile_height - 2*tile_gap;
182     CBaseTexture *texture = CTexture::LoadFromFile(files[i], width, height, CSettings::Get().GetBool("pictures.useexifrotation"), true);
183     if (texture && texture->GetWidth() && texture->GetHeight())
184     {
185       GetScale(texture->GetWidth(), texture->GetHeight(), width, height);
186
187       // scale appropriately
188       uint32_t *scaled = new uint32_t[width * height];
189       if (ScaleImage(texture->GetPixels(), texture->GetWidth(), texture->GetHeight(), texture->GetPitch(),
190                      (uint8_t *)scaled, width, height, width * 4))
191       {
192         if (!texture->GetOrientation() || OrientateImage(scaled, width, height, texture->GetOrientation()))
193         {
194           success = true; // Flag that we at least had one succesfull image processed
195           // drop into the texture
196           unsigned int posX = x*tile_width + (tile_width - width)/2;
197           unsigned int posY = y*tile_height + (tile_height - height)/2;
198           uint32_t *dest = buffer + posX + posY*g_advancedSettings.GetThumbSize();
199           uint32_t *src = scaled;
200           for (unsigned int y = 0; y < height; ++y)
201           {
202             memcpy(dest, src, width*4);
203             dest += g_advancedSettings.GetThumbSize();
204             src += width;
205           }
206         }
207       }
208       delete[] scaled;
209       delete texture;
210     }
211   }
212   // now save to a file
213   if (success)
214     success = CreateThumbnailFromSurface((uint8_t *)buffer, g_advancedSettings.GetThumbSize(), g_advancedSettings.GetThumbSize(),
215                                       g_advancedSettings.GetThumbSize() * 4, thumb);
216
217   free(buffer);
218   return success;
219 }
220
221 void CPicture::GetScale(unsigned int width, unsigned int height, unsigned int &out_width, unsigned int &out_height)
222 {
223   float aspect = (float)width / height;
224   if ((unsigned int)(out_width / aspect + 0.5f) > out_height)
225     out_width = (unsigned int)(out_height * aspect + 0.5f);
226   else
227     out_height = (unsigned int)(out_width / aspect + 0.5f);
228 }
229
230 bool CPicture::ScaleImage(uint8_t *in_pixels, unsigned int in_width, unsigned int in_height, unsigned int in_pitch,
231                           uint8_t *out_pixels, unsigned int out_width, unsigned int out_height, unsigned int out_pitch)
232 {
233   DllSwScale dllSwScale;
234   dllSwScale.Load();
235   struct SwsContext *context = dllSwScale.sws_getContext(in_width, in_height, PIX_FMT_BGRA,
236                                                          out_width, out_height, PIX_FMT_BGRA,
237                                                          SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL);
238
239   uint8_t *src[] = { in_pixels, 0, 0, 0 };
240   int     srcStride[] = { (int)in_pitch, 0, 0, 0 };
241   uint8_t *dst[] = { out_pixels , 0, 0, 0 };
242   int     dstStride[] = { (int)out_pitch, 0, 0, 0 };
243
244   if (context)
245   {
246     dllSwScale.sws_scale(context, src, srcStride, 0, in_height, dst, dstStride);
247     dllSwScale.sws_freeContext(context);
248     return true;
249   }
250   return false;
251 }
252
253 bool CPicture::OrientateImage(uint32_t *&pixels, unsigned int &width, unsigned int &height, int orientation)
254 {
255   // ideas for speeding these functions up: http://cgit.freedesktop.org/pixman/tree/pixman/pixman-fast-path.c
256   bool out = false;
257   switch (orientation)
258   {
259     case 1:
260       out = FlipHorizontal(pixels, width, height);
261       break;
262     case 2:
263       out = Rotate180CCW(pixels, width, height);
264       break;
265     case 3:
266       out = FlipVertical(pixels, width, height);
267       break;
268     case 4:
269       out = Transpose(pixels, width, height);
270       break;
271     case 5:
272       out = Rotate270CCW(pixels, width, height);
273       break;
274     case 6:
275       out = TransposeOffAxis(pixels, width, height);
276       break;
277     case 7:
278       out = Rotate90CCW(pixels, width, height);
279       break;
280     default:
281       CLog::Log(LOGERROR, "Unknown orientation %i", orientation);
282       break;
283   }
284   return out;
285 }
286
287 bool CPicture::FlipHorizontal(uint32_t *&pixels, unsigned int &width, unsigned int &height)
288 {
289   // this can be done in-place easily enough
290   for (unsigned int y = 0; y < height; ++y)
291   {
292     uint32_t *line = pixels + y * width;
293     for (unsigned int x = 0; x < width / 2; ++x)
294       std::swap(line[x], line[width - 1 - x]);
295   }
296   return true;
297 }
298
299 bool CPicture::FlipVertical(uint32_t *&pixels, unsigned int &width, unsigned int &height)
300 {
301   // this can be done in-place easily enough
302   for (unsigned int y = 0; y < height / 2; ++y)
303   {
304     uint32_t *line1 = pixels + y * width;
305     uint32_t *line2 = pixels + (height - 1 - y) * width;
306     for (unsigned int x = 0; x < width; ++x)
307       std::swap(*line1++, *line2++);
308   }
309   return true;
310 }
311
312 bool CPicture::Rotate180CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
313 {
314   // this can be done in-place easily enough
315   for (unsigned int y = 0; y < height / 2; ++y)
316   {
317     uint32_t *line1 = pixels + y * width;
318     uint32_t *line2 = pixels + (height - 1 - y) * width + width - 1;
319     for (unsigned int x = 0; x < width; ++x)
320       std::swap(*line1++, *line2--);
321   }
322   if (height % 2)
323   { // height is odd, so flip the middle row as well
324     uint32_t *line = pixels + (height - 1)/2 * width;
325     for (unsigned int x = 0; x < width / 2; ++x)
326       std::swap(line[x], line[width - 1 - x]);
327   }
328   return true;
329 }
330
331 bool CPicture::Rotate90CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
332 {
333   uint32_t *dest = new uint32_t[width * height * 4];
334   if (dest)
335   {
336     unsigned int d_height = width, d_width = height;
337     for (unsigned int y = 0; y < d_height; y++)
338     {
339       const uint32_t *src = pixels + (d_height - 1 - y); // y-th col from right, starting at top
340       uint32_t *dst = dest + d_width * y;                // y-th row from top, starting at left
341       for (unsigned int x = 0; x < d_width; x++)
342       {
343         *dst++ = *src;
344         src += width;
345       }
346     }
347     delete[] pixels;
348     pixels = dest;
349     std::swap(width, height);
350     return true;
351   }
352   return false;
353 }
354
355 bool CPicture::Rotate270CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
356 {
357   uint32_t *dest = new uint32_t[width * height * 4];
358   if (!dest)
359     return false;
360
361   unsigned int d_height = width, d_width = height;
362   for (unsigned int y = 0; y < d_height; y++)
363   {
364     const uint32_t *src = pixels + width * (d_width - 1) + y; // y-th col from left, starting at bottom
365     uint32_t *dst = dest + d_width * y;                       // y-th row from top, starting at left
366     for (unsigned int x = 0; x < d_width; x++)
367     {
368       *dst++ = *src;
369       src -= width;
370     }
371   }
372
373   delete[] pixels;
374   pixels = dest;
375   std::swap(width, height);
376   return true;
377 }
378
379 bool CPicture::Transpose(uint32_t *&pixels, unsigned int &width, unsigned int &height)
380 {
381   uint32_t *dest = new uint32_t[width * height * 4];
382   if (!dest)
383     return false;
384
385   unsigned int d_height = width, d_width = height;
386   for (unsigned int y = 0; y < d_height; y++)
387   {
388     const uint32_t *src = pixels + y;   // y-th col from left, starting at top
389     uint32_t *dst = dest + d_width * y; // y-th row from top, starting at left
390     for (unsigned int x = 0; x < d_width; x++)
391     {
392       *dst++ = *src;
393       src += width;
394     }
395   }
396
397   delete[] pixels;
398   pixels = dest;
399   std::swap(width, height);
400   return true;
401 }
402
403 bool CPicture::TransposeOffAxis(uint32_t *&pixels, unsigned int &width, unsigned int &height)
404 {
405   uint32_t *dest = new uint32_t[width * height * 4];
406   if (!dest)
407     return false;
408
409   unsigned int d_height = width, d_width = height;
410   for (unsigned int y = 0; y < d_height; y++)
411   {
412     const uint32_t *src = pixels + width * (d_width - 1) + (d_height - 1 - y); // y-th col from right, starting at bottom
413     uint32_t *dst = dest + d_width * y;                                        // y-th row, starting at left
414     for (unsigned int x = 0; x < d_width; x++)
415     {
416       *dst++ = *src;
417       src -= width;
418     }
419   }
420
421   delete[] pixels;
422   pixels = dest;
423   std::swap(width, height);
424   return true;
425 }