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