H300: * Implemented lcd_yuv_blit(). Speeds up video playback by about 7%. No bounds check in lcd_yuv_blit() (by convention), implementations for other targets should be adapted. * Fixed off-by-one bug in lcd_update_rect()


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10484 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugin.c b/apps/plugin.c
index 4474d0d..bbc5d7d 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -463,8 +463,8 @@
     lcd_remote_bitmap,
 #endif
 
-#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO) && \
-    !defined(SIMULATOR)
+#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO \
+     || CONFIG_LCD == LCD_H300) && !defined(SIMULATOR)
     lcd_yuv_blit,
 #endif
 
diff --git a/apps/plugin.h b/apps/plugin.h
index b764d86..f43e0ae 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -104,7 +104,7 @@
 #define PLUGIN_MAGIC 0x526F634B /* RocK */
 
 /* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 27
+#define PLUGIN_API_VERSION 28
 
 /* update this to latest version if a change to the api struct breaks
    backwards compatibility (and please take the opportunity to sort in any
@@ -539,8 +539,8 @@
     void (*lcd_remote_bitmap)(const fb_remote_data *src, int x, int y, int width,
                               int height);
 #endif
-#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO) && \
-    !defined(SIMULATOR)
+#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO \
+     || CONFIG_LCD == LCD_H300) &&  !defined(SIMULATOR)
     void (*lcd_yuv_blit)(unsigned char * const src[3],
                          int src_x, int src_y, int stride,
                          int x, int y, int width, int height);
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index 786d9d0..a5fdf5e 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -35,12 +35,14 @@
 #define CSUB_X 2
 #define CSUB_Y 2
 
-static int image_x;
-static int image_y;
 static int image_width;
 static int image_height;
 static int image_chroma_x;
 static int image_chroma_y;
+static int output_x;
+static int output_y;
+static int output_width;
+static int output_height;
 
 #if (LCD_DEPTH == 16) && \
     ((LCD_PIXELFORMAT == RGB565) || (LCD_PIXELFORMAT == RGB565SWAPPED))
@@ -197,16 +199,16 @@
     (void)id;
     (void)instance;
 
-#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO) && \
-    !defined(SIMULATOR)
+#if (CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO \
+     || CONFIG_LCD == LCD_H300) && !defined(SIMULATOR)
     rb->lcd_yuv_blit(buf,
                     0,0,image_width,
-                    image_x,image_y,image_width,image_height);
+                    output_x,output_y,output_width,output_height);
 #elif (LCD_DEPTH == 16) && \
     ((LCD_PIXELFORMAT == RGB565) || (LCD_PIXELFORMAT == RGB565SWAPPED))
     yuv_bitmap_part(buf,0,0,image_width,
-                    image_x,image_y,image_width,image_height);
-    rb->lcd_update_rect(image_x,image_y,image_width,image_height);
+                    output_x,output_y,output_width,output_height);
+    rb->lcd_update_rect(output_x,output_y,output_width,output_height);
 #endif
 
     if (starttick==0) starttick=*rb->current_tick-1; /* Avoid divby0 */
@@ -265,15 +267,19 @@
     image_chroma_y=image_height/chroma_height;
 
     if (image_width >= LCD_WIDTH) {
-        image_x = 0;
+        output_width = LCD_WIDTH;
+        output_x = 0;
     } else {
-        image_x = (LCD_WIDTH-image_width)/2;
+        output_width = image_width;
+        output_x = (LCD_WIDTH-image_width)/2;
     }
 
     if (image_height >= LCD_HEIGHT) {
-        image_y = 0;
+        output_height = LCD_HEIGHT;
+        output_y = 0;
     } else {
-        image_y = (LCD_HEIGHT-image_height)/2;
+        output_height = image_height;
+        output_y = (LCD_HEIGHT-image_height)/2;
     }
 
     return 0;
diff --git a/firmware/drivers/lcd-h300.c b/firmware/drivers/lcd-h300.c
index 0bdb123..c3f5d48 100644
--- a/firmware/drivers/lcd-h300.c
+++ b/firmware/drivers/lcd-h300.c
@@ -77,18 +77,20 @@
 #define R_HORIZ_RAM_ADDR_POS    0x44
 #define R_VERT_RAM_ADDR_POS     0x45
 
+#define LCD_CMD  (*(volatile unsigned short *)0xf0000000)
+#define LCD_DATA (*(volatile unsigned short *)0xf0000002)
 
 /* called very frequently - inline! */
 static inline void lcd_write_reg(int reg, int val)
 {
-    *(volatile unsigned short *)0xf0000000 = reg;
-    *(volatile unsigned short *)0xf0000002 = val;
+    LCD_CMD = reg;
+    LCD_DATA = val;
 }
 
 /* called very frequently - inline! */
 static inline void lcd_begin_write_gram(void)
 {
-    *(volatile unsigned short *)0xf0000000 = R_WRITE_DATA_2_GRAM;
+    LCD_CMD = R_WRITE_DATA_2_GRAM;
 }
 
 /*** hardware configuration ***/
@@ -299,6 +301,135 @@
     /*if(display_on)*/
 }
 
+#define CSUB_X 2
+#define CSUB_Y 2
+
+#define RYFAC (31*257)
+#define GYFAC (63*257)
+#define BYFAC (31*257)
+#define RVFAC 11170     /* 31 * 257 *  1.402    */
+#define GVFAC (-11563)  /* 63 * 257 * -0.714136 */
+#define GUFAC (-5572)   /* 63 * 257 * -0.344136 */
+#define BUFAC 14118     /* 31 * 257 *  1.772    */
+
+#define ROUNDOFFS (127*257)
+
+/* Performance function to blit a YUV bitmap directly to the LCD */
+void lcd_yuv_blit(unsigned char * const src[3],
+                  int src_x, int src_y, int stride,
+                  int x, int y, int width, int height)
+{
+    if (display_on)
+    {
+        int ymax;
+        
+        width  = (width + 1) & ~1;
+        height = (height + 1) & ~1;
+        ymax = y + height - 1;
+
+        /* set update window */
+
+        /* horiz ram addr */ 
+        lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (ymax << 8) | y);
+        
+        /* vert ram addr */ 
+        lcd_write_reg(R_VERT_RAM_ADDR_POS,((x+xoffset+width-1) << 8) | (x+xoffset));
+        lcd_write_reg(R_RAM_ADDR_SET, ((x+xoffset) << 8) | y);
+        lcd_begin_write_gram(); 
+
+        for (; y <= ymax; y++)
+        {
+            /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
+            const unsigned char *ysrc = src[0] + stride * src_y + src_x;
+            const unsigned char *usrc = src[1] + (stride/CSUB_X) * (src_y/CSUB_Y)
+                                               + (src_x/CSUB_X);
+            const unsigned char *vsrc = src[2] + (stride/CSUB_X) * (src_y/CSUB_Y)
+                                               + (src_x/CSUB_X);
+            const unsigned char *row_end = ysrc + width;
+
+            int y, u, v;
+            int rc, gc, bc;
+            int red, green, blue;
+            unsigned rbits, gbits, bbits;
+
+            do
+            {
+                u = *usrc++ - 128;
+                v = *vsrc++ - 128;
+                rc = RVFAC * v + ROUNDOFFS;
+                gc = GVFAC * v + GUFAC * u + ROUNDOFFS;
+                bc = BUFAC * u + ROUNDOFFS;
+
+                y = *ysrc++;
+                red   = RYFAC * y + rc;
+                green = GYFAC * y + gc;
+                blue  = BYFAC * y + bc;
+
+                if ((unsigned)red > (RYFAC*255+ROUNDOFFS))
+                {
+                    if (red < 0)
+                        red = 0;
+                    else
+                        red = (RYFAC*255+ROUNDOFFS);
+                }
+                if ((unsigned)green > (GYFAC*255+ROUNDOFFS))
+                {
+                    if (green < 0)
+                        green = 0;
+                    else
+                        green = (GYFAC*255+ROUNDOFFS);
+                }
+                if ((unsigned)blue > (BYFAC*255+ROUNDOFFS))
+                {
+                    if (blue < 0)
+                        blue = 0;
+                    else
+                        blue = (BYFAC*255+ROUNDOFFS);
+                }
+                rbits = ((unsigned)red) >> 16 ;
+                gbits = ((unsigned)green) >> 16 ;
+                bbits = ((unsigned)blue) >> 16 ;
+
+                LCD_DATA = (rbits << 11) | (gbits << 5) | bbits;
+
+                y = *ysrc++;
+                red   = RYFAC * y + rc;
+                green = GYFAC * y + gc;
+                blue  = BYFAC * y + bc;
+
+                if ((unsigned)red > (RYFAC*255+ROUNDOFFS))
+                {
+                    if (red < 0)
+                        red = 0;
+                    else
+                        red = (RYFAC*255+ROUNDOFFS);
+                }
+                if ((unsigned)green > (GYFAC*255+ROUNDOFFS))
+                {
+                    if (green < 0)
+                        green = 0;
+                    else
+                        green = (GYFAC*255+ROUNDOFFS);
+                }
+                if ((unsigned)blue > (BYFAC*255+ROUNDOFFS))
+                {
+                    if (blue < 0)
+                        blue = 0;
+                    else
+                        blue = (BYFAC*255+ROUNDOFFS);
+                }
+                rbits = ((unsigned)red) >> 16 ;
+                gbits = ((unsigned)green) >> 16 ;
+                bbits = ((unsigned)blue) >> 16 ;
+
+                LCD_DATA = (rbits << 11) | (gbits << 5) | bbits;
+            }
+            while (ysrc < row_end);
+
+            src_y++;
+        }
+    }
+}
 
 /* Update the display.
    This must be called after all other LCD functions that change the display. */
@@ -324,8 +455,8 @@
 void lcd_update_rect(int, int, int, int) ICODE_ATTR;
 void lcd_update_rect(int x, int y, int width, int height)
 {
-    if(display_on) {    
-        int ymax = y + height;
+    if(display_on) {
+        int ymax = y + height - 1;
 
         if(x + width > LCD_WIDTH)
             width = LCD_WIDTH - x;
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index 32a958a..7551a1a 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -71,7 +71,8 @@
                                   int style);
 extern void lcd_icon(int icon, bool enable);
 
-#if CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO
+#if CONFIG_LCD == LCD_IPODCOLOR || CONFIG_LCD == LCD_IPODNANO \
+    || CONFIG_LCD == LCD_H300
 void lcd_yuv_blit(unsigned char * const src[3],
                   int src_x, int src_y, int stride,
                   int x, int y, int width, int height);