Merge branch 'master' of git.opendreambox.org:/git/enigma2
[enigma2.git] / lib / gdi / gpixmap.cpp
1 #include <cstdlib>
2 #include <cstring>
3 #include <lib/gdi/gpixmap.h>
4 #include <lib/gdi/region.h>
5 #include <lib/gdi/accel.h>
6
7 gLookup::gLookup()
8         :size(0), lookup(0)
9 {
10 }
11
12 gLookup::gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end)
13         :size(0), lookup(0)
14 {
15         build(size, pal, start, end);
16 }
17
18 void gLookup::build(int _size, const gPalette &pal, const gRGB &start, const gRGB &end)
19 {
20         if (lookup)
21         {
22                 delete [] lookup;
23                 lookup=0;
24                 size=0;
25         }
26         size=_size;
27         if (!size)
28                 return;
29         lookup=new gColor[size];
30         
31         for (int i=0; i<size; i++)
32         {
33                 gRGB col;
34                 if (i)
35                 {
36                         int rdiff=-start.r+end.r;
37                         int gdiff=-start.g+end.g;
38                         int bdiff=-start.b+end.b;
39                         int adiff=-start.a+end.a;
40                         rdiff*=i; rdiff/=(size-1);
41                         gdiff*=i; gdiff/=(size-1);
42                         bdiff*=i; bdiff/=(size-1);
43                         adiff*=i; adiff/=(size-1);
44                         col.r=start.r+rdiff;
45                         col.g=start.g+gdiff;
46                         col.b=start.b+bdiff;
47                         col.a=start.a+adiff;
48                 } else
49                         col=start;
50                 lookup[i]=pal.findColor(col);
51         }
52 }
53
54 gSurface::gSurface()
55 {
56         x = 0;
57         y = 0;
58         bpp = 0;
59         bypp = 0;
60         stride = 0;
61         data = 0;
62         data_phys = 0;
63         clut.colors = 0;
64         clut.data = 0;
65         type = 0;
66 }
67
68 gSurface::gSurface(eSize size, int _bpp, int accel)
69 {
70         x = size.width();
71         y = size.height();
72         bpp = _bpp;
73
74         switch (bpp)
75         {
76         case 8:
77                 bypp = 1;
78                 break;
79         case 15:
80         case 16:
81                 bypp = 2;
82                 break;
83         case 24:                // never use 24bit mode
84         case 32:
85                 bypp = 4;
86                 break;
87         default:
88                 bypp = (bpp+7)/8;
89         }
90
91         stride = x*bypp;
92         
93         data = 0;
94         data_phys = 0;
95         
96         if (accel)
97         {
98                 stride += 63;
99                 stride &=~63;
100                 
101                 int pal_size = 0;
102                 if (bpp == 8)
103                         pal_size = 256 * 4;
104                 
105                 if (gAccel::getInstance())
106                         eDebug("accel memory: %d", gAccel::getInstance()->accelAlloc(data, data_phys, y * stride + pal_size));
107                 else
108                         eDebug("no accel available");
109         }
110         
111         clut.colors = 0;
112         clut.data = 0;
113
114         if (!data)
115                 data = new unsigned char [y * stride];
116         
117         type = 1;
118 }
119
120 gSurface::~gSurface()
121 {
122         if (type)
123         {
124                 if (data_phys)
125                         gAccel::getInstance()->accelFree(data_phys);
126                 else
127                         delete [] (unsigned char*)data;
128
129                 delete [] clut.data;
130         }
131 }
132
133 gPixmap *gPixmap::lock()
134 {
135         contentlock.lock(1);
136         return this;
137 }
138
139 void gPixmap::unlock()
140 {
141         contentlock.unlock(1);
142 }
143
144 void gPixmap::fill(const gRegion &region, const gColor &color)
145 {
146         unsigned int i;
147         for (i=0; i<region.rects.size(); ++i)
148         {
149                 const eRect &area = region.rects[i];
150                 if (area.empty())
151                         continue;
152
153                 if (surface->bpp == 8)
154                 {
155                         for (int y=area.top(); y<area.bottom(); y++)
156                                 memset(((__u8*)surface->data)+y*surface->stride+area.left(), color.color, area.width());
157                 } else if (surface->bpp == 32)
158                 {
159                         __u32 col;
160
161                         if (surface->clut.data && color < surface->clut.colors)
162                                 col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
163                         else
164                                 col=0x10101*color;
165                         
166                         col^=0xFF000000;
167                         
168                         if (surface->data_phys && gAccel::getInstance())
169                                 if (!gAccel::getInstance()->fill(surface,  area, col))
170                                         continue;
171
172                         for (int y=area.top(); y<area.bottom(); y++)
173                         {
174                                 __u32 *dst=(__u32*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
175                                 int x=area.width();
176                                 while (x--)
177                                         *dst++=col;
178                         }
179                 }       else
180                         eWarning("couldn't fill %d bpp", surface->bpp);
181         }
182 }
183
184 void gPixmap::fill(const gRegion &region, const gRGB &color)
185 {
186         unsigned int i;
187         for (i=0; i<region.rects.size(); ++i)
188         {
189                 const eRect &area = region.rects[i];
190                 if (area.empty())
191                         continue;
192
193                 if (surface->bpp == 32)
194                 {
195                         __u32 col;
196
197                         col = color.argb();
198                         col^=0xFF000000;
199
200                         if (surface->data_phys && gAccel::getInstance())
201                                 if (!gAccel::getInstance()->fill(surface,  area, col))
202                                         continue;
203
204                         for (int y=area.top(); y<area.bottom(); y++)
205                         {
206                                 __u32 *dst=(__u32*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
207                                 int x=area.width();
208                                 while (x--)
209                                         *dst++=col;
210                         }
211                 }       else
212                         eWarning("couldn't rgbfill %d bpp", surface->bpp);
213         }
214 }
215
216 static void blit_8i_to_32(__u32 *dst, __u8 *src, __u32 *pal, int width)
217 {
218         while (width--)
219                 *dst++=pal[*src++];
220 }
221
222 static void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width)
223 {
224         while (width--)
225         {
226                 if (!(pal[*src]&0x80000000))
227                 {
228                         src++;
229                         dst++;
230                 } else
231                         *dst++=pal[*src++];
232         }
233 }
234
235                 /* WARNING, this function is not endian safe! */
236 static void blit_8i_to_32_ab(__u32 *dst, __u8 *src, __u32 *pal, int width)
237 {
238         while (width--)
239         {
240 #define BLEND(x, y, a) (y + (((x-y) * a)>>8))
241                 __u32 srccol = pal[*src++];
242                 __u32 dstcol = *dst;
243                 unsigned char sb = srccol & 0xFF;
244                 unsigned char sg = (srccol >> 8) & 0xFF;
245                 unsigned char sr = (srccol >> 16) & 0xFF;
246                 unsigned char sa = (srccol >> 24) & 0xFF;
247
248                 unsigned char db = dstcol & 0xFF;
249                 unsigned char dg = (dstcol >> 8) & 0xFF;
250                 unsigned char dr = (dstcol >> 16) & 0xFF;
251                 unsigned char da = (dstcol >> 24) & 0xFF;
252
253                 da = BLEND(0xFF, da, sa) & 0xFF;
254                 dr = BLEND(sr, dr, sa) & 0xFF;
255                 dg = BLEND(sg, dg, sa) & 0xFF;
256                 db = BLEND(sb, db, sa) & 0xFF;
257
258 #undef BLEND
259                 *dst++ = db | (dg << 8) | (dr << 16) | (da << 24);
260         }
261 }
262
263 #define FIX 0x10000
264
265 void gPixmap::blit(const gPixmap &src, const eRect &_pos, const gRegion &clip, int flag)
266 {
267 //      eDebug("blit: -> %d.%d %d:%d -> %d.%d %d:%d, flags=%d",
268 //              _pos.x(), _pos.y(), _pos.width(), _pos.height(),
269 //              clip.extends.x(), clip.extends.y(), clip.extends.width(), clip.extends.height(),
270 //              flag);
271         eRect pos = _pos;
272         
273 //      eDebug("source size: %d %d", src.size().width(), src.size().height());
274         
275         if (!(flag & blitScale)) /* pos' size is valid only when scaling */
276                 pos = eRect(pos.topLeft(), src.size());
277         else if (pos.size() == src.size()) /* no scaling required */
278                 flag &= ~blitScale;
279
280         int scale_x = FIX, scale_y = FIX;
281         
282         if (flag & blitScale)
283         {
284                 ASSERT(src.size().width());
285                 ASSERT(src.size().height());
286                 scale_x = pos.size().width() * FIX / src.size().width();
287                 scale_y = pos.size().height() * FIX / src.size().height();
288         }
289         
290 //      eDebug("SCALE %x %x", scale_x, scale_y);
291
292         for (unsigned int i=0; i<clip.rects.size(); ++i)
293         {
294 //              eDebug("clip rect: %d %d %d %d", clip.rects[i].x(), clip.rects[i].y(), clip.rects[i].width(), clip.rects[i].height());
295                 eRect area = pos; /* pos is the virtual (pre-clipping) area on the dest, which can be larger/smaller than src if scaling is enabled */
296                 area&=clip.rects[i];
297                 area&=eRect(ePoint(0, 0), size());
298
299                 if (area.empty())
300                         continue;
301
302                 eRect srcarea = area;
303                 srcarea.moveBy(-pos.x(), -pos.y());
304
305 //              eDebug("srcarea before scale: %d %d %d %d",
306 //                      srcarea.x(), srcarea.y(), srcarea.width(), srcarea.height());
307                 
308                 if (flag & blitScale)
309                         srcarea = eRect(srcarea.x() * FIX / scale_x, srcarea.y() * FIX / scale_y, srcarea.width() * FIX / scale_x, srcarea.height() * FIX / scale_y);
310
311 //              eDebug("srcarea after scale: %d %d %d %d",
312 //                      srcarea.x(), srcarea.y(), srcarea.width(), srcarea.height());
313
314                 if ((surface->data_phys && src.surface->data_phys) && (gAccel::getInstance()))
315                         if (!gAccel::getInstance()->blit(surface, src.surface, area, srcarea, flag))
316                                 continue;
317
318                 if (flag & blitScale)
319                 {
320                         eWarning("unimplemented: scale on non-accel surfaces");
321                         continue;
322                 }
323
324                 if ((surface->bpp == 8) && (src.surface->bpp==8))
325                 {
326                         __u8 *srcptr=(__u8*)src.surface->data;
327                         __u8 *dstptr=(__u8*)surface->data;
328
329                         srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
330                         dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
331                         for (int y=0; y<area.height(); y++)
332                         {
333                                 if (flag & (blitAlphaTest|blitAlphaBlend))
334                                 {
335                       // no real alphatest yet
336                                         int width=area.width();
337                                         unsigned char *src=(unsigned char*)srcptr;
338                                         unsigned char *dst=(unsigned char*)dstptr;
339                                                 // use duff's device here!
340                                         while (width--)
341                                         {
342                                                 if (!*src)
343                                                 {
344                                                         src++;
345                                                         dst++;
346                                                 } else
347                                                         *dst++=*src++;
348                                         }
349                                 } else
350                                         memcpy(dstptr, srcptr, area.width()*surface->bypp);
351                                 srcptr+=src.surface->stride;
352                                 dstptr+=surface->stride;
353                         }
354                 } else if ((surface->bpp == 32) && (src.surface->bpp==32))
355                 {
356                         __u32 *srcptr=(__u32*)src.surface->data;
357                         __u32 *dstptr=(__u32*)surface->data;
358
359                         srcptr+=srcarea.left()+srcarea.top()*src.surface->stride/4;
360                         dstptr+=area.left()+area.top()*surface->stride/4;
361                         for (int y=0; y<area.height(); y++)
362                         {
363                                 if (flag & blitAlphaTest)
364                                 {
365                                         int width=area.width();
366                                         unsigned long *src=(unsigned long*)srcptr;
367                                         unsigned long *dst=(unsigned long*)dstptr;
368
369                                         while (width--)
370                                         {
371                                                 if (!((*src)&0xFF000000))
372                                                 {
373                                                         src++;
374                                                         dst++;
375                                                 } else
376                                                         *dst++=*src++;
377                                         }
378                                 } else if (flag & blitAlphaBlend)
379                                 {
380                                         // uh oh. this is only until hardware accel is working.
381
382                                         int width=area.width();
383                                                         // ARGB color space!
384                                         unsigned char *src=(unsigned char*)srcptr;
385                                         unsigned char *dst=(unsigned char*)dstptr;
386
387 #define BLEND(x, y, a) (y + ((x-y) * a)/256)
388                                         while (width--)
389                                         {
390                                                 unsigned char sa = src[3];
391                                                 unsigned char sr = src[2];
392                                                 unsigned char sg = src[1];
393                                                 unsigned char sb = src[0];
394
395                                                 unsigned char da = dst[3];
396                                                 unsigned char dr = dst[2];
397                                                 unsigned char dg = dst[1];
398                                                 unsigned char db = dst[0];
399
400                                                 dst[3] = BLEND(0xFF, da, sa);
401                                                 dst[2] = BLEND(sr, dr, sa);
402                                                 dst[1] = BLEND(sg, dg, sa);
403                                                 dst[0] = BLEND(sb, db, sa);
404 #undef BLEND
405
406                                                 src += 4; dst += 4;
407                                         }
408                                 } else
409                                         memcpy(dstptr, srcptr, area.width()*surface->bypp);
410                                 srcptr+=src.surface->stride/4;
411                                 dstptr+=surface->stride/4;
412                         }
413                 } else if ((surface->bpp == 32) && (src.surface->bpp==8))
414                 {       
415                         __u8 *srcptr=(__u8*)src.surface->data;
416                         __u8 *dstptr=(__u8*)surface->data; // !!
417                         __u32 pal[256];
418
419                         for (int i=0; i<256; ++i)
420                         {
421                                 if (src.surface->clut.data && (i<src.surface->clut.colors))
422                                         pal[i]=(src.surface->clut.data[i].a<<24)|(src.surface->clut.data[i].r<<16)|(src.surface->clut.data[i].g<<8)|(src.surface->clut.data[i].b);
423                                 else
424                                         pal[i]=0x010101*i;
425                                 pal[i]^=0xFF000000;
426                         }
427
428                         srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
429                         dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
430                         for (int y=0; y<area.height(); y++)
431                         {
432                                 int width=area.width();
433                                 unsigned char *psrc=(unsigned char*)srcptr;
434                                 __u32 *dst=(__u32*)dstptr;
435                                 if (flag & blitAlphaTest)
436                                         blit_8i_to_32_at(dst, psrc, pal, width);
437                                 else if (flag & blitAlphaBlend)
438                                         blit_8i_to_32_ab(dst, psrc, pal, width);
439                                 else
440                                         blit_8i_to_32(dst, psrc, pal, width);
441                                 srcptr+=src.surface->stride;
442                                 dstptr+=surface->stride;
443                         }
444                 } else
445                         eWarning("cannot blit %dbpp from %dbpp", surface->bpp, src.surface->bpp);
446         }
447 }
448
449 #undef FIX
450
451 void gPixmap::mergePalette(const gPixmap &target)
452 {
453         if ((!surface->clut.colors) || (!target.surface->clut.colors))
454                 return;
455
456         gColor *lookup=new gColor[surface->clut.colors];
457
458         for (int i=0; i<surface->clut.colors; i++)
459                 lookup[i].color=target.surface->clut.findColor(surface->clut.data[i]);
460         
461         delete [] surface->clut.data;
462         surface->clut.colors=target.surface->clut.colors;
463         surface->clut.data=new gRGB[surface->clut.colors];
464         memcpy(surface->clut.data, target.surface->clut.data, sizeof(gRGB)*surface->clut.colors);
465
466         __u8 *dstptr=(__u8*)surface->data;
467
468         for (int ay=0; ay<surface->y; ay++)
469         {
470                 for (int ax=0; ax<surface->x; ax++)
471                         dstptr[ax]=lookup[dstptr[ax]];
472                 dstptr+=surface->stride;
473         }
474         
475         delete [] lookup;
476 }
477
478 static inline int sgn(int a)
479 {
480         if (a < 0)
481                 return -1;
482         else if (!a)
483                 return 0;
484         else
485                 return 1;
486 }
487
488 void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color)
489 {
490         __u8 *srf8 = 0;
491         __u32 *srf32 = 0; 
492         int stride = surface->stride;
493         
494         if (clip.rects.empty())
495                 return;
496                 
497         __u32 col = 0;
498         if (surface->bpp == 8)
499         {
500                 srf8 = (__u8*)surface->data;
501         } else if (surface->bpp == 32)
502         {
503                 srf32 = (__u32*)surface->data;
504                 
505                 if (surface->clut.data && color < surface->clut.colors)
506                         col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
507                 else
508                         col=0x10101*color;
509                 col^=0xFF000000;                        
510         }
511         
512         int xa = start.x(), ya = start.y(), xb = dst.x(), yb = dst.y();
513         int dx, dy, x, y, s1, s2, e, temp, swap, i;
514         dy=abs(yb-ya);
515         dx=abs(xb-xa);
516         s1=sgn(xb-xa);
517         s2=sgn(yb-ya);
518         x=xa;
519         y=ya;
520         if (dy>dx)
521         {
522                 temp=dx;
523                 dx=dy;
524                 dy=temp;
525                 swap=1;
526         } else
527                 swap=0;
528         e = 2*dy-dx;
529         
530         int lasthit = 0;
531         for(i=1; i<=dx; i++)
532         {
533                                 /* i don't like this clipping loop, but the only */
534                                 /* other choice i see is to calculate the intersections */
535                                 /* before iterating through the pixels. */
536                                 
537                                 /* one could optimize this because of the ordering */
538                                 /* of the bands. */
539                                 
540                 lasthit = 0;
541                 int a = lasthit;
542                 
543                         /* if last pixel was invisble, first check bounding box */
544                 if (a == -1)
545                 {
546                                 /* check if we just got into the bbox again */
547                         if (clip.extends.contains(x, y))
548                                 lasthit = a = 0;
549                         else
550                                 goto fail;
551                 } else if (!clip.rects[a].contains(x, y))
552                 {
553                         do
554                         {
555                                 ++a;
556                                 if ((unsigned int)a == clip.rects.size())
557                                         a = 0;
558                                 if (a == lasthit)
559                                 {
560                                         goto fail;
561                                         lasthit = -1;
562                                 }
563                         } while (!clip.rects[a].contains(x, y));
564                         lasthit = a;
565                 }
566                 
567                 if (srf8)
568                         srf8[y * stride + x] = color;
569                 if (srf32)
570                         srf32[y * stride/4 + x] = col;
571 fail:
572                 while (e>=0)
573                 {
574                         if (swap==1) x+=s1;
575                         else y+=s2;
576                         e-=2*dx;
577                 }
578     if (swap==1)
579         y+=s2;
580                 else
581                         x+=s1;
582                 e+=2*dy;
583         }
584 }
585
586 gColor gPalette::findColor(const gRGB &rgb) const
587 {
588                 /* grayscale? */
589         if (!data)
590                 return (rgb.r + rgb.g + rgb.b) / 3;
591         
592         int difference=1<<30, best_choice=0;
593         for (int t=0; t<colors; t++)
594         {
595                 int ttd;
596                 int td=(signed)(rgb.r-data[t].r); td*=td; td*=(255-data[t].a);
597                 ttd=td;
598                 if (ttd>=difference)
599                         continue;
600                 td=(signed)(rgb.g-data[t].g); td*=td; td*=(255-data[t].a);
601                 ttd+=td;
602                 if (ttd>=difference)
603                         continue;
604                 td=(signed)(rgb.b-data[t].b); td*=td; td*=(255-data[t].a);
605                 ttd+=td;
606                 if (ttd>=difference)
607                         continue;
608                 td=(signed)(rgb.a-data[t].a); td*=td; td*=255;
609                 ttd+=td;
610                 if (ttd>=difference)
611                         continue;
612                 if (!ttd)
613                         return t;
614                 difference=ttd;
615                 best_choice=t;
616         }
617         return best_choice;
618 }
619
620 DEFINE_REF(gPixmap);
621
622 gPixmap::~gPixmap()
623 {
624         if (must_delete_surface)
625                 delete surface;
626 }
627
628 gPixmap::gPixmap(gSurface *surface)
629         :surface(surface), must_delete_surface(false)
630 {
631 }
632
633 gPixmap::gPixmap(eSize size, int bpp, int accel)
634         :must_delete_surface(true)
635 {
636         surface = new gSurface(size, bpp, accel);
637 }
638