fix color oled support
[enigma2.git] / lib / gdi / gpixmap.cpp
index d0eff03f00f40d7f5a937f5e2a3bd9b6471346dc..0e8d39ecbc8cdb53011cb29ff1022d097eeaeece 100644 (file)
@@ -1,6 +1,13 @@
+#include <cstdlib>
+#include <cstring>
 #include <lib/gdi/gpixmap.h>
 #include <lib/gdi/region.h>
 #include <lib/gdi/accel.h>
+#include <byteswap.h>
+
+#ifndef BYTE_ORDER
+#error "no BYTE_ORDER defined!"
+#endif
 
 gLookup::gLookup()
        :size(0), lookup(0)
@@ -96,8 +103,12 @@ gSurface::gSurface(eSize size, int _bpp, int accel)
                stride += 63;
                stride &=~63;
                
+               int pal_size = 0;
+               if (bpp == 8)
+                       pal_size = 256 * 4;
+               
                if (gAccel::getInstance())
-                       eDebug("accel memory: %d", gAccel::getInstance()->accelAlloc(data, data_phys, y * stride));
+                       eDebug("accel memory: %d", gAccel::getInstance()->accelAlloc(data, data_phys, y * stride + pal_size));
                else
                        eDebug("no accel available");
        }
@@ -141,13 +152,33 @@ void gPixmap::fill(const gRegion &region, const gColor &color)
        for (i=0; i<region.rects.size(); ++i)
        {
                const eRect &area = region.rects[i];
-               if ((area.height()<=0) || (area.width()<=0))
+               if (area.empty())
                        continue;
 
                if (surface->bpp == 8)
                {
                        for (int y=area.top(); y<area.bottom(); y++)
                                memset(((__u8*)surface->data)+y*surface->stride+area.left(), color.color, area.width());
+               } else if (surface->bpp == 16)
+               {
+                       __u32 icol;
+
+                       if (surface->clut.data && color < surface->clut.colors)
+                               icol=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
+                       else
+                               icol=0x10101*color;
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       __u16 col = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19);
+#else
+                       __u16 col = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19;
+#endif
+                       for (int y=area.top(); y<area.bottom(); y++)
+                       {
+                               __u16 *dst=(__u16*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
+                               int x=area.width();
+                               while (x--)
+                                       *dst++=col;
+                       }
                } else if (surface->bpp == 32)
                {
                        __u32 col;
@@ -157,8 +188,8 @@ void gPixmap::fill(const gRegion &region, const gColor &color)
                        else
                                col=0x10101*color;
                        
-                       col^=0xFF000000;                        
-
+                       col^=0xFF000000;
+                       
                        if (surface->data_phys && gAccel::getInstance())
                                if (!gAccel::getInstance()->fill(surface,  area, col))
                                        continue;
@@ -181,7 +212,7 @@ void gPixmap::fill(const gRegion &region, const gRGB &color)
        for (i=0; i<region.rects.size(); ++i)
        {
                const eRect &area = region.rects[i];
-               if ((area.height()<=0) || (area.width()<=0))
+               if (area.empty())
                        continue;
 
                if (surface->bpp == 32)
@@ -202,40 +233,163 @@ void gPixmap::fill(const gRegion &region, const gRGB &color)
                                while (x--)
                                        *dst++=col;
                        }
+               } else if (surface->bpp == 16)
+               {
+                       __u32 icol = color.argb();
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       __u16 col = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19);
+#else
+                       __u16 col = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19;
+#endif
+                       for (int y=area.top(); y<area.bottom(); y++)
+                       {
+                               __u16 *dst=(__u16*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
+                               int x=area.width();
+                               while (x--)
+                                       *dst++=col;
+                       }
                }       else
                        eWarning("couldn't rgbfill %d bpp", surface->bpp);
        }
 }
 
-void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag)
+static inline void blit_8i_to_32(__u32 *dst, __u8 *src, __u32 *pal, int width)
+{
+       while (width--)
+               *dst++=pal[*src++];
+}
+
+static inline void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width)
+{
+       while (width--)
+       {
+               if (!(pal[*src]&0x80000000))
+               {
+                       src++;
+                       dst++;
+               } else
+                       *dst++=pal[*src++];
+       }
+}
+
+static inline void blit_8i_to_16(__u16 *dst, __u8 *src, __u32 *pal, int width)
+{
+       while (width--)
+               *dst++=pal[*src++] & 0xFFFF;
+}
+
+static inline void blit_8i_to_16_at(__u16 *dst, __u8 *src, __u32 *pal, int width)
+{
+       while (width--)
+       {
+               if (!(pal[*src]&0x80000000))
+               {
+                       src++;
+                       dst++;
+               } else
+                       *dst++=pal[*src++] & 0xFFFF;
+       }
+}
+
+               /* WARNING, this function is not endian safe! */
+static void blit_8i_to_32_ab(__u32 *dst, __u8 *src, __u32 *pal, int width)
+{
+       while (width--)
+       {
+#define BLEND(x, y, a) (y + (((x-y) * a)>>8))
+               __u32 srccol = pal[*src++];
+               __u32 dstcol = *dst;
+               unsigned char sb = srccol & 0xFF;
+               unsigned char sg = (srccol >> 8) & 0xFF;
+               unsigned char sr = (srccol >> 16) & 0xFF;
+               unsigned char sa = (srccol >> 24) & 0xFF;
+
+               unsigned char db = dstcol & 0xFF;
+               unsigned char dg = (dstcol >> 8) & 0xFF;
+               unsigned char dr = (dstcol >> 16) & 0xFF;
+               unsigned char da = (dstcol >> 24) & 0xFF;
+
+               da = BLEND(0xFF, da, sa) & 0xFF;
+               dr = BLEND(sr, dr, sa) & 0xFF;
+               dg = BLEND(sg, dg, sa) & 0xFF;
+               db = BLEND(sb, db, sa) & 0xFF;
+
+#undef BLEND
+               *dst++ = db | (dg << 8) | (dr << 16) | (da << 24);
+       }
+}
+
+#define FIX 0x10000
+
+void gPixmap::blit(const gPixmap &src, const eRect &_pos, const gRegion &clip, int flag)
 {
+//     eDebug("blit: -> %d.%d %d:%d -> %d.%d %d:%d, flags=%d",
+//             _pos.x(), _pos.y(), _pos.width(), _pos.height(),
+//             clip.extends.x(), clip.extends.y(), clip.extends.width(), clip.extends.height(),
+//             flag);
+       eRect pos = _pos;
+       
+//     eDebug("source size: %d %d", src.size().width(), src.size().height());
+       
+       if (!(flag & blitScale)) /* pos' size is valid only when scaling */
+               pos = eRect(pos.topLeft(), src.size());
+       else if (pos.size() == src.size()) /* no scaling required */
+               flag &= ~blitScale;
+
+       int scale_x = FIX, scale_y = FIX;
+       
+       if (flag & blitScale)
+       {
+               ASSERT(src.size().width());
+               ASSERT(src.size().height());
+               scale_x = pos.size().width() * FIX / src.size().width();
+               scale_y = pos.size().height() * FIX / src.size().height();
+       }
+       
+//     eDebug("SCALE %x %x", scale_x, scale_y);
+
        for (unsigned int i=0; i<clip.rects.size(); ++i)
        {
-               eRect area=eRect(pos, src.size());
+//             eDebug("clip rect: %d %d %d %d", clip.rects[i].x(), clip.rects[i].y(), clip.rects[i].width(), clip.rects[i].height());
+               eRect area = pos; /* pos is the virtual (pre-clipping) area on the dest, which can be larger/smaller than src if scaling is enabled */
                area&=clip.rects[i];
                area&=eRect(ePoint(0, 0), size());
 
-               if ((area.width()<0) || (area.height()<0))
+               if (area.empty())
                        continue;
 
-               eRect srcarea=area;
+               eRect srcarea = area;
                srcarea.moveBy(-pos.x(), -pos.y());
+
+//             eDebug("srcarea before scale: %d %d %d %d",
+//                     srcarea.x(), srcarea.y(), srcarea.width(), srcarea.height());
                
+               if (flag & blitScale)
+                       srcarea = eRect(srcarea.x() * FIX / scale_x, srcarea.y() * FIX / scale_y, srcarea.width() * FIX / scale_x, srcarea.height() * FIX / scale_y);
+
+//             eDebug("srcarea after scale: %d %d %d %d",
+//                     srcarea.x(), srcarea.y(), srcarea.width(), srcarea.height());
+
                if ((surface->data_phys && src.surface->data_phys) && (gAccel::getInstance()))
-                       if (!gAccel::getInstance()->blit(surface, src.surface, area.topLeft(), srcarea, flag))
+                       if (!gAccel::getInstance()->blit(surface, src.surface, area, srcarea, flag))
                                continue;
-               flag &= ~ blitAlphaBlend;
-               
+
+               if (flag & blitScale)
+               {
+                       eWarning("unimplemented: scale on non-accel surfaces");
+                       continue;
+               }
+
                if ((surface->bpp == 8) && (src.surface->bpp==8))
                {
                        __u8 *srcptr=(__u8*)src.surface->data;
                        __u8 *dstptr=(__u8*)surface->data;
-       
+
                        srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
                        dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
                        for (int y=0; y<area.height(); y++)
                        {
-                               if (flag & blitAlphaTest)
+                               if (flag & (blitAlphaTest|blitAlphaBlend))
                                {
                      // no real alphatest yet
                                        int width=area.width();
@@ -260,7 +414,7 @@ void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag
                {
                        __u32 *srcptr=(__u32*)src.surface->data;
                        __u32 *dstptr=(__u32*)surface->data;
-       
+
                        srcptr+=srcarea.left()+srcarea.top()*src.surface->stride/4;
                        dstptr+=area.left()+area.top()*surface->stride/4;
                        for (int y=0; y<area.height(); y++)
@@ -283,7 +437,7 @@ void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag
                                } else if (flag & blitAlphaBlend)
                                {
                                        // uh oh. this is only until hardware accel is working.
-                                       
+
                                        int width=area.width();
                                                        // ARGB color space!
                                        unsigned char *src=(unsigned char*)srcptr;
@@ -301,12 +455,13 @@ void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag
                                                unsigned char dr = dst[2];
                                                unsigned char dg = dst[1];
                                                unsigned char db = dst[0];
-                                               
+
                                                dst[3] = BLEND(0xFF, da, sa);
                                                dst[2] = BLEND(sr, dr, sa);
                                                dst[1] = BLEND(sg, dg, sa);
                                                dst[0] = BLEND(sb, db, sa);
-                                               
+#undef BLEND
+
                                                src += 4; dst += 4;
                                        }
                                } else
@@ -328,34 +483,108 @@ void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag
                                        pal[i]=0x010101*i;
                                pal[i]^=0xFF000000;
                        }
-       
+
                        srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
                        dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
                        for (int y=0; y<area.height(); y++)
                        {
+                               int width=area.width();
+                               unsigned char *psrc=(unsigned char*)srcptr;
+                               __u32 *dst=(__u32*)dstptr;
+                               if (flag & blitAlphaTest)
+                                       blit_8i_to_32_at(dst, psrc, pal, width);
+                               else if (flag & blitAlphaBlend)
+                                       blit_8i_to_32_ab(dst, psrc, pal, width);
+                               else
+                                       blit_8i_to_32(dst, psrc, pal, width);
+                               srcptr+=src.surface->stride;
+                               dstptr+=surface->stride;
+                       }
+               } else if ((surface->bpp == 16) && (src.surface->bpp==8))
+               {
+                       __u8 *srcptr=(__u8*)src.surface->data;
+                       __u8 *dstptr=(__u8*)surface->data; // !!
+                       __u32 pal[256];
+
+                       for (int i=0; i<256; ++i)
+                       {
+                               __u32 icol;
+                               if (src.surface->clut.data && (i<src.surface->clut.colors))
+                                       icol=(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);
+                               else
+                                       icol=0x010101*i;
+#if BYTE_ORDER == LITTLE_ENDIAN
+                               pal[i] = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19);
+#else
+                               pal[i] = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19;
+#endif
+                               pal[i]^=0xFF000000;
+                       }
+
+                       srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
+                       dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
+
+                       if (flag & blitAlphaBlend)
+                               eWarning("ignore unsupported 8bpp -> 16bpp alphablend!");
+
+                       for (int y=0; y<area.height(); y++)
+                       {
+                               int width=area.width();
+                               unsigned char *psrc=(unsigned char*)srcptr;
+                               __u16 *dst=(__u16*)dstptr;
+                               if (flag & blitAlphaTest)
+                                       blit_8i_to_16_at(dst, psrc, pal, width);
+                               else
+                                       blit_8i_to_16(dst, psrc, pal, width);
+                               srcptr+=src.surface->stride;
+                               dstptr+=surface->stride;
+                       }
+               } else if ((surface->bpp == 16) && (src.surface->bpp==32))
+               {
+                       __u8 *srcptr=(__u8*)src.surface->data;
+                       __u8 *dstptr=(__u8*)surface->data;
+
+                       srcptr+=srcarea.left()+srcarea.top()*src.surface->stride;
+                       dstptr+=area.left()+area.top()*surface->stride;
+
+                       if (flag & blitAlphaBlend)
+                               eWarning("ignore unsupported 32bpp -> 16bpp alphablend!");
+
+                       for (int y=0; y<area.height(); y++)
+                       {
+                               int width=area.width();
+                               __u32 *srcp=(__u32*)srcptr;
+                               __u16 *dstp=(__u16*)dstptr;
+
                                if (flag & blitAlphaTest)
                                {
-                     // no real alphatest yet
-                                       int width=area.width();
-                                       unsigned char *src=(unsigned char*)srcptr;
-                                       __u32 *dst=(__u32*)dstptr;
-                                               // use duff's device here!
                                        while (width--)
                                        {
-                                               if (!(pal[*src]&0x80000000))
+                                               if (!((*srcp)&0xFF000000))
                                                {
-                                                       src++;
-                                                       dst++;
+                                                       srcp++;
+                                                       dstp++;
                                                } else
-                                                       *dst++=pal[*src++];
+                                               {
+                                                       __u32 icol = *srcp++;
+#if BYTE_ORDER == LITTLE_ENDIAN
+                                                       *dstp++ = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19);
+#else
+                                                       *dstp++ = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19;
+#endif
+                                               }
                                        }
                                } else
                                {
-                                       int width=area.width();
-                                       unsigned char *src=(unsigned char*)srcptr;
-                                       __u32 *dst=(__u32*)dstptr;
                                        while (width--)
-                                               *dst++=pal[*src++];
+                                       {
+                                               __u32 icol = *srcp++;
+#if BYTE_ORDER == LITTLE_ENDIAN
+                                               *dstp++ = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19);
+#else
+                                               *dstp++ = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19;
+#endif
+                                       }
                                }
                                srcptr+=src.surface->stride;
                                dstptr+=surface->stride;
@@ -365,6 +594,8 @@ void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag
        }
 }
 
+#undef FIX
+
 void gPixmap::mergePalette(const gPixmap &target)
 {
        if ((!surface->clut.colors) || (!target.surface->clut.colors))
@@ -405,27 +636,34 @@ static inline int sgn(int a)
 void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color)
 {
        __u8 *srf8 = 0;
-       __u32 *srf32 = 0; 
+       __u16 *srf16 = 0;
+       __u32 *srf32 = 0;
        int stride = surface->stride;
-       
+
        if (clip.rects.empty())
                return;
-               
+
+       __u16 col16;
        __u32 col = 0;
        if (surface->bpp == 8)
-       {
                srf8 = (__u8*)surface->data;
-       } else if (surface->bpp == 32)
+       else
        {
                srf32 = (__u32*)surface->data;
-               
                if (surface->clut.data && color < surface->clut.colors)
                        col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
                else
                        col=0x10101*color;
-               col^=0xFF000000;                        
+               col^=0xFF000000;
        }
-       
+
+       if (surface->bpp == 16)
+#if BYTE_ORDER == LITTLE_ENDIAN
+               col16=bswap_16(((col & 0xFF) >> 3) << 11 | ((col & 0xFF00) >> 10) << 5 | (col & 0xFF0000) >> 19);
+#else
+               col16=((col & 0xFF) >> 3) << 11 | ((col & 0xFF00) >> 10) << 5 | (col & 0xFF0000) >> 19;
+#endif
+
        int xa = start.x(), ya = start.y(), xb = dst.x(), yb = dst.y();
        int dx, dy, x, y, s1, s2, e, temp, swap, i;
        dy=abs(yb-ya);
@@ -443,7 +681,7 @@ void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color)
        } else
                swap=0;
        e = 2*dy-dx;
-       
+
        int lasthit = 0;
        for(i=1; i<=dx; i++)
        {
@@ -480,20 +718,25 @@ void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color)
                        } while (!clip.rects[a].contains(x, y));
                        lasthit = a;
                }
-               
+
                if (srf8)
                        srf8[y * stride + x] = color;
-               if (srf32)
+               else if (srf16)
+                       srf16[y * stride/2 + x] = col16;
+               else
                        srf32[y * stride/4 + x] = col;
 fail:
                while (e>=0)
                {
-                       if (swap==1) x+=s1;
-                       else y+=s2;
+                       if (swap==1)
+                               x+=s1;
+                       else
+                               y+=s2;
                        e-=2*dx;
                }
-    if (swap==1)
-       y+=s2;
+
+               if (swap==1)
+                       y+=s2;
                else
                        x+=s1;
                e+=2*dy;