[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDOverlayRenderer.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 "utils/log.h"
22 #include "DVDOverlayRenderer.h"
23 #include "DVDCodecs/Overlay/DVDOverlaySpu.h"
24 #include "DVDCodecs/Overlay/DVDOverlayText.h"
25 #include "DVDCodecs/Overlay/DVDOverlayImage.h"
26 #include "DVDCodecs/Overlay/DVDOverlaySSA.h"
27
28 #define CLAMP(a, min, max) ((a) > (max) ? (max) : ( (a) < (min) ? (min) : a ))
29
30
31 void CDVDOverlayRenderer::Render(DVDPictureRenderer* pPicture, CDVDOverlay* pOverlay, double pts)
32 {
33   if (pOverlay->IsOverlayType(DVDOVERLAY_TYPE_SPU))
34   {
35     // display subtitle, if bForced is true, it's a menu overlay and we should crop it
36     Render_SPU_YUV(pPicture, pOverlay, pOverlay->bForced);
37   }
38   else if (pOverlay->IsOverlayType(DVDOVERLAY_TYPE_IMAGE))
39   {
40     Render(pPicture, (CDVDOverlayImage*)pOverlay);
41   }
42   else if (pOverlay->IsOverlayType(DVDOVERLAY_TYPE_SSA))
43   {
44     Render(pPicture, (CDVDOverlaySSA*)pOverlay, pts);
45   }
46   else if (false && pOverlay->IsOverlayType(DVDOVERLAY_TYPE_TEXT))
47   {
48     CDVDOverlayText* pOverlayText = (CDVDOverlayText*)pOverlay;
49
50     //CLog::Log(LOGDEBUG, " - s: %i, e: %i", (int)(pOverlayText->iPTSStartTime / 1000), (int)(pOverlayText->iPTSStopTime / 1000));
51
52     CDVDOverlayText::CElement* e = pOverlayText->m_pHead;
53     while (e)
54     {
55       if (e->IsElementType(CDVDOverlayText::ELEMENT_TYPE_TEXT))
56       {
57         CDVDOverlayText::CElementText* t = (CDVDOverlayText::CElementText*)e;
58         CLog::Log(LOGDEBUG, " - %s", t->m_text);
59       }
60       e = e->pNext;
61     }
62   }
63 }
64
65
66 void CDVDOverlayRenderer::Render(DVDPictureRenderer* pPicture, CDVDOverlaySSA* pOverlay, double pts)
67 {
68
69   int height, width;
70   height = pPicture->height;
71   width = pPicture->width;
72
73   ASS_Image* img = pOverlay->m_libass->RenderImage(width, height, pts);
74
75   while(img)
76   {
77     DWORD color = img->color;
78     BYTE alpha = (BYTE)(color &0xff);
79
80     // fully transparent or width or height is 0 -> not displayed
81     if(alpha == 255 || img->w == 0 || img->h == 0)
82     {
83       img = img->next;
84       continue;
85     }
86
87     //ASS_Image colors are RGBA
88     double r = ((color >> 24) & 0xff) / 255.0;
89     double g = ((color >> 16) & 0xff) / 255.0;
90     double b = ((color >> 8 ) & 0xff) / 255.0;
91
92     BYTE luma  = (BYTE)(        255 * CLAMP( 0.299 * r + 0.587 * g + 0.114 * b,  0.0, 1.0));
93     BYTE v     = (BYTE)(127.5 + 255 * CLAMP( 0.500 * r - 0.419 * g - 0.081 * b, -0.5, 0.5));
94     BYTE u     = (BYTE)(127.5 + 255 * CLAMP(-0.169 * r - 0.331 * g + 0.500 * b, -0.5, 0.5));
95
96     int y = std::max(0,std::min(img->dst_y, pPicture->height-img->h));
97     int x = std::max(0,std::min(img->dst_x, pPicture->width-img->w));
98
99     for(int i=0; i<img->h; i++)
100     {
101       if(y + i >= pPicture->height)
102         break;
103
104       BYTE* line = img->bitmap + img->stride*i;
105
106       BYTE* target[3];
107       target[0] = pPicture->data[0] + pPicture->stride[0]*(i + y) + x;
108       target[1] = pPicture->data[1] + pPicture->stride[1]*((i + y)>>1) + (x>>1);
109       target[2] = pPicture->data[2] + pPicture->stride[2]*((i + y)>>1) + (x>>1);
110
111       for(int j=0; j<img->w; j++)
112       {
113         if(x + j >= pPicture->width)
114           break;
115
116         unsigned char index, opacity, k;
117         index = line[j];
118
119         //Blend the image with the underlying picture
120         opacity = 255 - alpha;
121         k = (unsigned char)index * opacity / 255;
122
123         target[0][j]    = (k*luma + (255-k)*target[0][j])/255;
124         target[1][j>>1] = (k*u    + (255-k)*target[1][j>>1])/255;
125         target[2][j>>1] = (k*v    + (255-k)*target[2][j>>1])/255;
126       }
127     }
128     img = img->next;
129   }
130 }
131
132 void CDVDOverlayRenderer::Render(DVDPictureRenderer* pPicture, CDVDOverlayImage* pOverlay)
133 {
134   BYTE* palette[4];
135   for(int i=0;i<4;i++)
136     palette[i] = (BYTE*)calloc(1, pOverlay->palette_colors);
137
138   for(int i=0;i<pOverlay->palette_colors;i++)
139   {
140     uint32_t color = pOverlay->palette[i];
141
142     palette[3][i] = (BYTE)((color >> 24) & 0xff);
143
144     double r = ((color >> 16) & 0xff) / 255.0;
145     double g = ((color >> 8 ) & 0xff) / 255.0;
146     double b = ((color >> 0 ) & 0xff) / 255.0;
147
148     palette[0][i] = (BYTE)(255 * CLAMP(0.299 * r + 0.587 * g + 0.114 * b, 0.0, 1.0));
149     palette[1][i] = (BYTE)(127.5 + 255 * CLAMP( 0.500 * r - 0.419 * g - 0.081 * b, -0.5, 0.5));
150     palette[2][i] = (BYTE)(127.5 + 255 * CLAMP(-0.169 * r - 0.331 * g + 0.500 * b, -0.5, 0.5));
151   }
152
153   // we try o fit it in if it's outside the image
154   int y = std::max(0,std::min(pOverlay->y, pPicture->height-pOverlay->height));
155   int x = std::max(0,std::min(pOverlay->x, pPicture->width-pOverlay->width));
156
157   for(int i=0;i<pOverlay->height;i++)
158   {
159     if(y + i >= pPicture->height)
160       break;
161
162     BYTE* line = pOverlay->data + pOverlay->linesize*i;
163
164     BYTE* target[3];
165     target[0] = pPicture->data[0] + pPicture->stride[0]*(i + y) + x;
166     target[1] = pPicture->data[1] + pPicture->stride[1]*((i + y)>>1) + (x>>1);
167     target[2] = pPicture->data[2] + pPicture->stride[2]*((i + y)>>1) + (x>>1);
168
169     for(int j=0;j<pOverlay->width;j++)
170     {
171       if(x + j >= pPicture->width)
172         break;
173
174       unsigned char index = line[j];
175       if(index > pOverlay->palette_colors)
176       {
177         CLog::Log(LOGWARNING, "%s - out of range color index %u", __FUNCTION__, index);
178         continue;
179       }
180
181       if(palette[3][index] == 0)
182         continue;
183
184       int s_blend = palette[3][index] + 1;
185       int t_blend = 256 - s_blend;
186
187       target[0][j] = (target[0][j] * t_blend + palette[0][index] * s_blend) >> 8;
188       if(!(1&(i|j)))
189       {
190         target[1][j>>1] = (target[1][j>>1] * t_blend + palette[1][index] * s_blend) >> 8;
191         target[2][j>>1] = (target[2][j>>1] * t_blend + palette[2][index] * s_blend) >> 8;
192       }
193     }
194
195   }
196   for(int i=0;i<4;i++)
197     free(palette[i]);
198 }
199
200 // render the parsed sub (parsed rle) onto the yuv image
201 void CDVDOverlayRenderer::Render_SPU_YUV(DVDPictureRenderer* pPicture, CDVDOverlay* pOverlaySpu, bool bCrop)
202 {
203   CDVDOverlaySpu* pOverlay = (CDVDOverlaySpu*)pOverlaySpu;
204
205   unsigned __int8*  p_destptr = NULL;
206   unsigned __int16* p_source = (unsigned __int16*)pOverlay->result;
207   unsigned __int8*  p_dest[3];
208
209   int i_x, i_y;
210   int rp_len, i_color, pixels_to_draw;
211   unsigned __int16 i_colprecomp, i_destalpha;
212
213   int btn_x_start = pOverlay->crop_i_x_start;
214   int btn_x_end   = pOverlay->crop_i_x_end;
215   int btn_y_start = pOverlay->crop_i_y_start;
216   int btn_y_end   = pOverlay->crop_i_y_end;
217
218   int *p_color;
219   int p_alpha;
220
221   p_dest[0] = pPicture->data[0] + pPicture->stride[0] * pOverlay->y;
222   p_dest[1] = pPicture->data[1] + pPicture->stride[1] * (pOverlay->y >> 1);
223   p_dest[2] = pPicture->data[2] + pPicture->stride[2] * (pOverlay->y >> 1);
224
225   /* Draw until we reach the bottom of the subtitle */
226   for (i_y = pOverlay->y; i_y < pOverlay->y + pOverlay->height; i_y++)
227   {
228     /* Draw until we reach the end of the line */
229     for (i_x = pOverlay->x; i_x < pOverlay->x + pOverlay->width ; i_x += rp_len)
230     {
231       /* Get the RLE part, then draw the line */
232       i_color = *p_source & 0x3;
233       rp_len = *p_source++ >> 2;
234
235       while( rp_len > 0 )
236       {
237         pixels_to_draw = rp_len;
238
239         p_color = pOverlay->color[i_color];
240         p_alpha = pOverlay->alpha[i_color];
241
242         if (bCrop)
243         {
244           if (i_y >= btn_y_start && i_y <= btn_y_end)
245           {
246             if (i_x < btn_x_start && i_x + rp_len >= btn_x_start) // starts outside
247               pixels_to_draw = btn_x_start - i_x;
248             else if( i_x >= btn_x_start && i_x <= btn_x_end ) // starts inside
249             {
250               p_color = pOverlay->highlight_color[i_color];
251               p_alpha = pOverlay->highlight_alpha[i_color];
252               pixels_to_draw = btn_x_end - i_x + 1; // don't draw part that is outside
253             }
254           }
255           /* make sure we are not requested to draw to far */
256           /* that part will be taken care of in next pass */
257           if( pixels_to_draw > rp_len )
258             pixels_to_draw = rp_len;
259         }
260
261         switch (p_alpha)
262         {
263         case 0x00:
264           break;
265
266         case 0x0f:
267           memset(p_dest[0] + i_x, p_color[0], pixels_to_draw);
268           if (!(i_y & 1)) // Only draw even lines
269           {
270             memset(p_dest[1] + (i_x >> 1), p_color[2], pixels_to_draw >> 1);
271             memset(p_dest[2] + (i_x >> 1), p_color[1], pixels_to_draw >> 1);
272           }
273           break;
274
275         default:
276           /* To be able to divide by 16 (>>4) we add 1 to the alpha.
277             * This means Alpha 0 won't be completely transparent, but
278             * that's handled in a special case above anyway. */
279           // First we deal with Y
280           i_colprecomp = (unsigned __int16)p_color[0]
281                         * (unsigned __int16)(p_alpha + 1);
282           i_destalpha = 15 - p_alpha;
283
284           for (p_destptr = p_dest[0] + i_x; p_destptr < p_dest[0] + i_x + pixels_to_draw; p_destptr++)
285           {
286             *p_destptr = (( i_colprecomp + (unsigned __int16) * p_destptr * i_destalpha ) >> 4) & 0xFF;
287           }
288
289           if (!(i_y & 1)) // Only draw even lines
290           {
291             // now U
292             i_colprecomp = (unsigned __int16)p_color[2]
293                           * (unsigned __int16)(p_alpha + 1);
294             for ( p_destptr = p_dest[1] + (i_x >> 1); p_destptr < p_dest[1] + ((i_x + pixels_to_draw) >> 1); p_destptr++)
295             {
296               *p_destptr = (( i_colprecomp + (unsigned __int16) * p_destptr * i_destalpha ) >> 4) & 0xFF;
297             }
298             // and finally V
299             i_colprecomp = (unsigned __int16)p_color[1]
300                           * (unsigned __int16)(p_alpha + 1);
301             for ( p_destptr = p_dest[2] + (i_x >> 1); p_destptr < p_dest[2] + ((i_x + pixels_to_draw) >> 1); p_destptr++)
302             {
303               *p_destptr = (( i_colprecomp + (unsigned __int16) * p_destptr * i_destalpha ) >> 4) & 0xFF;
304             }
305           }
306           break;
307         }
308
309         /* add/subtract what we just drew */
310         rp_len -= pixels_to_draw;
311         i_x += pixels_to_draw;
312       }
313     }
314
315     p_dest[0] += pPicture->stride[0];
316     if (i_y & 1)
317       continue;
318     p_dest[1] += pPicture->stride[1];
319     p_dest[2] += pPicture->stride[2];
320   }
321 }