diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS
index ca9665a..9d8da7d 100644
--- a/apps/plugins/imageviewer/SUBDIRS
+++ b/apps/plugins/imageviewer/SUBDIRS
@@ -1,5 +1,3 @@
 bmp
 jpeg
-#ifdef HAVE_LCD_COLOR
 png
-#endif
diff --git a/apps/plugins/imageviewer/png/png.c b/apps/plugins/imageviewer/png/png.c
index 4643060..bf63178 100644
--- a/apps/plugins/imageviewer/png/png.c
+++ b/apps/plugins/imageviewer/png/png.c
@@ -52,11 +52,26 @@ The manual and changelog can be found in the header file "lodepng.h"
 You are free to name this file lodepng.cpp or lodepng.c depending on your usage.
 */
 
+/* supported chunk types:
+ * mandatory:
+ *     IHDR
+ *     PLTE
+ *     IDAT
+ *     IEND
+ *
+ * optional:
+ *     pHYs
+ *     tRNS
+ *     bKGD
+ *     tIME - commented out
+ */
+
 #include "plugin.h"
 #include "lcd.h"
 #include <lib/pluginlib_bmp.h>
 #include "zlib.h"
 #include "png.h"
+#include "bmp.h"
 
 /* ////////////////////////////////////////////////////////////////////////// */
 /* LodeFlate & LodeZlib Setting structs                                       */
@@ -79,6 +94,7 @@ typedef struct LodePNG_InfoColor /*info about the color type of an image*/
     unsigned key_b;       /*blue component of color key*/
 } LodePNG_InfoColor;
 
+#if 0
 typedef struct LodePNG_Time /*LodePNG's encoder does not generate the current time. To make it add a time chunk the correct time has to be provided*/
 {
     unsigned      year;    /*2 bytes*/
@@ -88,6 +104,7 @@ typedef struct LodePNG_Time /*LodePNG's encoder does not generate the current ti
     unsigned char minute;  /*0-59*/
     unsigned char second;  /*0-60 (to allow for leap seconds)*/
 } LodePNG_Time;
+#endif
 
 typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/
 {
@@ -104,10 +121,11 @@ typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels
     unsigned background_r;       /*red component of suggested background color*/
     unsigned background_g;       /*green component of suggested background color*/
     unsigned background_b;       /*blue component of suggested background color*/
-
+#if 0
     /*time chunk (tIME)*/
     unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/
     LodePNG_Time time;
+#endif
 
     /*phys chunk (pHYs)*/
     unsigned      phys_defined; /*is pHYs chunk defined?*/
@@ -160,6 +178,46 @@ static size_t decoded_image_size;
 
 static LodePNG_Decoder _decoder;
 
+#ifdef HAVE_LCD_COLOR
+#define resize_bitmap   smooth_resize_bitmap
+#elif defined(USEGSLIB)
+#define resize_bitmap   grey_resize_bitmap
+#else
+#define resize_bitmap   simple_resize_bitmap
+#endif
+
+#ifdef USEGSLIB
+/* copied & pasted from lib/pluginlib_bmp.c. */
+static void grey_resize_bitmap(struct bitmap *src, struct bitmap *dst)
+{
+    const int srcw = src->width;
+    const int srch = src->height;
+    const int dstw = dst->width;
+    const int dsth = dst->height;
+    unsigned char *srcd = src->data;
+    unsigned char *dstd = dst->data;
+
+    const long xrstep = ((srcw-1) << 8) / (dstw-1);
+    const long yrstep = ((srch-1) << 8) / (dsth-1);
+    unsigned char *src_row, *dst_row;
+    long xr, yr = 0;
+    int src_x, src_y, dst_x, dst_y;
+    for (dst_y=0; dst_y < dsth; dst_y++)
+    {
+        src_y = (yr >> 8);
+        src_row = &srcd[src_y * srcw];
+        dst_row = &dstd[dst_y * dstw];
+        for (xr=0,dst_x=0; dst_x < dstw; dst_x++)
+        {
+            src_x = (xr >> 8);
+            dst_row[dst_x] = src_row[src_x];
+            xr += xrstep;
+        }
+        yr += yrstep;
+    }
+}
+#endif /* USEGSLIB */
+
 /*
 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
@@ -323,15 +381,15 @@ static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDe
 {
     switch (colorType)
     {
-    case 0:
+    case PNG_COLORTYPE_GREY:
         if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
-    case 2:
+    case PNG_COLORTYPE_RGB:
         if (!(                                 bd == 8 || bd == 16)) return 37; break; /*RGB*/
-    case 3:
+    case PNG_COLORTYPE_PALETTE:
         if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8            )) return 37; break; /*palette*/
-    case 4:
+    case PNG_COLORTYPE_GREYA:
         if (!(                                 bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
-    case 6:
+    case PNG_COLORTYPE_RGBA:
         if (!(                                 bd == 8 || bd == 16)) return 37; break; /*RGBA*/
     default:
         return 31;
@@ -343,15 +401,15 @@ static unsigned getNumColorChannels(unsigned colorType)
 {
     switch (colorType)
     {
-    case 0:
+    case PNG_COLORTYPE_GREY:
         return 1; /*grey*/
-    case 2:
+    case PNG_COLORTYPE_RGB:
         return 3; /*RGB*/
-    case 3:
+    case PNG_COLORTYPE_PALETTE:
         return 1; /*palette*/
-    case 4:
+    case PNG_COLORTYPE_GREYA:
         return 2; /*grey + alpha*/
-    case 6:
+    case PNG_COLORTYPE_RGBA:
         return 4; /*RGBA*/
     }
     return 0; /*unexisting color type*/
@@ -368,7 +426,7 @@ void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
 {
     info->key_defined = 0;
     info->key_r = info->key_g = info->key_b = 0;
-    info->colorType = 6;
+    info->colorType = PNG_COLORTYPE_RGBA;
     info->bitDepth = 8;
     memset(info->palette, 0, 256 * 4 * sizeof(unsigned char));
     info->palettesize = 0;
@@ -398,7 +456,7 @@ void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
     info->background_defined = 0;
     info->background_r = info->background_g = info->background_b = 0;
 
-    info->time_defined = 0;
+    /* info->time_defined = 0; */
     info->phys_defined = 0;
 }
 
@@ -460,11 +518,43 @@ converts from any color type to 24-bit or 32-bit (later maybe more supported). r
 the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type (LodePNG_InfoColor_getBpp)
 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
 */
-unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
+unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
 {
     size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
     size_t x, y;
-    unsigned char c;
+    unsigned int value;
+
+    /* line buffer for pixel format transformation */
+#ifdef HAVE_LCD_COLOR
+    struct uint8_rgb *line_buf = (struct uint8_rgb *)(out + w * h * FB_DATA_SZ);
+#else
+    unsigned char *line_buf = (unsigned char *)(out + w * h * FB_DATA_SZ);
+#endif
+
+    struct bitmap bm = {
+        .width = w,
+        .height = h,
+        .data = (unsigned char*)out,
+    };
+
+    struct scaler_context ctx = {
+        .bm = &bm,
+        .dither = 0,
+    };
+
+#ifdef USEGSLIB
+    const struct custom_format *cformat = &format_grey;
+#else
+    const struct custom_format *cformat = &format_native;
+#endif
+
+void (*output_row_8)(uint32_t, void*, struct scaler_context*) = cformat->output_row_8;
+
+#ifdef HAVE_LCD_COLOR
+struct uint8_rgb *pixel;
+#else
+unsigned char *pixel;
+#endif
 
     if (!running_slideshow)
     {
@@ -472,88 +562,85 @@ unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColo
         rb->lcd_update();
     }
 
-    /*cases where in and out already have the same format*/
-    if (LodePNG_InfoColor_equal(infoIn, infoOut))
-    {
-
-        i = 0;
-        j = 0;
-        for (y = 0 ; y < h ; y++) {
-            for (x = 0 ; x < w ; x++) {
-                unsigned char r = in[i++];
-                unsigned char g = in[i++];
-                unsigned char b = in[i++];
-                out[j++] = LCD_RGBPACK(r,g,b);
-            }
-        }
-        return 0;
-    }
-
-    if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
-    {
+#ifdef HAVE_LCD_COLOR
         if (infoIn->bitDepth == 8)
         {
             switch (infoIn->colorType)
             {
-            case 0: /*greyscale color*/
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
-                    for (x = 0 ; x < w ; x++) {
-                        c=in[i];
-                        //unsigned char r = in[i];
-                        //unsigned char g = in[i];
-                        //unsigned char b = in[i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                    /* reset line buf */
+                    pixel = line_buf;
+
+                    for (x = 0; x < w ; x++) {
+                        value = in[i++];
+                        pixel->red = (unsigned char)value;
+                        pixel->green = (unsigned char)value;
+                        pixel->blue = (unsigned char)value;
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 2: /*RGB color*/
+            case PNG_COLORTYPE_RGB: /*RGB color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        j = 3 * i;
-                        unsigned char r = in[j];
-                        unsigned char g = in[j + 1];
-                        unsigned char b = in[j + 2];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        j = 3*i++;
+                        pixel->red = *(in + j);
+                        pixel->green = *(in + j + 1);
+                        pixel->blue = *(in + j + 2);
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 3: /*indexed color (palette)*/
+            case PNG_COLORTYPE_PALETTE: /*indexed color (palette)*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    /* reset line buf */
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
                         if (in[i] >= infoIn->palettesize) return 46;
-                        j = in[i] << 2;
-                        unsigned char r = infoIn->palette[j];
-                        unsigned char g = infoIn->palette[j + 1];
-                        unsigned char b = infoIn->palette[j + 2];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        j = in[i++] << 2;
+
+                        pixel->red = *(infoIn->palette + j);
+                        pixel->green = *(infoIn->palette + j + 1);
+                        pixel->blue = *(infoIn->palette + j + 2);
+                        pixel++;
                     }
+                    output_row_8(y,(void *)(line_buf),&ctx);
                 }
                 break;
-            case 4: /*greyscale with alpha*/
+            case PNG_COLORTYPE_GREYA: /*greyscale with alpha*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i << 1];
-                        //unsigned char r = in[i<<1];
-                        //unsigned char g = in[i<<1];
-                        //unsigned char b = in[i<<1];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        value = in[i++ << 1];
+                        pixel->red = (unsigned char)value;
+                        pixel->green = (unsigned char)value;
+                        pixel->blue = (unsigned char)value;
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 6: /*RGB with alpha*/
+            case PNG_COLORTYPE_RGBA: /*RGB with alpha*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        j = i << 2;
-                        unsigned char r = in[j];
-                        unsigned char g = in[j + 1];
-                        unsigned char b = in[j + 2];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        j = i++ << 2;
+
+                        pixel->red = *(in + j);
+                        pixel->green = *(in + j +1);
+                        pixel->blue = *(in + j + 2);
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
             default:
@@ -564,52 +651,60 @@ unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColo
         {
             switch (infoIn->colorType)
             {
-            case 0: /*greyscale color*/
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i << 1];
-                        //unsigned char r = in[2 * i];
-                        //unsigned char g = in[2 * i];
-                        //unsigned char b = in[2 * i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        value = in[i++ << 1];
+                        pixel->red = (unsigned char)value;
+                        pixel->green = (unsigned char)value;
+                        pixel->blue = (unsigned char)value;
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 2: /*RGB color*/
+            case PNG_COLORTYPE_RGB: /*RGB color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        j = 6 * i;
-                        unsigned char r = in[j];
-                        unsigned char g = in[j + 2];
-                        unsigned char b = in[j + 4];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        j = 6 * i++;
+                        pixel->red = *(in + j);
+                        pixel->green = *(in + j + 2);
+                        pixel->blue = *(in + j + 4);
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 4: /*greyscale with alpha*/
+            case PNG_COLORTYPE_GREYA: /*greyscale with alpha*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i << 2];
-                        //unsigned char r = in[4 * i];
-                        //unsigned char g = in[4 * i];
-                        //unsigned char b = in[4 * i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        value = in[i++ << 2];
+                        pixel->red = (unsigned char)value;
+                        pixel->green = (unsigned char)value;
+                        pixel->blue = (unsigned char)value;
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 6: /*RGB with alpha*/
+            case PNG_COLORTYPE_RGBA: /*RGB with alpha*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        j = i << 3;
-                        unsigned char r = in[j];
-                        unsigned char g = in[j + 2];
-                        unsigned char b = in[j + 4];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        j = i++ << 3;
+                        pixel->red = *(in + j);
+                        pixel->green = *(in + j + 2);
+                        pixel->blue = *(in + j + 4);
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
             default:
@@ -620,123 +715,232 @@ unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColo
         {
             switch (infoIn->colorType)
             {
-            case 0: /*greyscale color*/
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
+                        value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
                         value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
-                        unsigned char r = (unsigned char)value;
-                        unsigned char g = (unsigned char)value;
-                        unsigned char b = (unsigned char)value;
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        pixel->red = (unsigned char)value;
+                        pixel->green = (unsigned char)value;
+                        pixel->blue = (unsigned char)value;
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 3: /*indexed color (palette)*/
+            case PNG_COLORTYPE_PALETTE: /*indexed color (palette)*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
+                        value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
                         if (value >= infoIn->palettesize) return 47;
                         j = value << 2;
-                        unsigned char r = infoIn->palette[j];
-                        unsigned char g = infoIn->palette[j + 1];
-                        unsigned char b = infoIn->palette[j + 2];
-                        out[i++] = LCD_RGBPACK(r,g,b);
+                        pixel->red = infoIn->palette[j];
+                        pixel->green = infoIn->palette[j + 1];
+                        pixel->blue = infoIn->palette[j + 2];
+                        pixel++;
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
             default:
                 break;
             }
         }
-    }
-    else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
-    {
-        if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
+#else /* greyscale targets */
+struct uint8_rgb px_rgb;
+
         if (infoIn->bitDepth == 8)
         {
             switch (infoIn->colorType)
             {
-            case 0: /*greyscale color*/
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    /* reset line buf */
+                    pixel = line_buf;
+
+                    for (x = 0; x < w ; x++) {
+                        value = in[i++];
+                        px_rgb.red = (unsigned char)value;
+                        px_rgb.green = (unsigned char)value;
+                        px_rgb.blue = (unsigned char)value;
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
+                }
+                break;
+            case PNG_COLORTYPE_RGB: /*RGB color*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i];
-                        //unsigned char r = in[i];
-                        //unsigned char g = in[i];
-                        //unsigned char b = in[i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        j = 3*i++;
+                        px_rgb.red = *(in + j);
+                        px_rgb.green = *(in + j + 1);
+                        px_rgb.blue = *(in + j + 2);
+                        *pixel++ = brightness(px_rgb);
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 4: /*greyscale with alpha*/
+            case PNG_COLORTYPE_PALETTE: /*indexed color (palette)*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    /* reset line buf */
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[(i << 1) + 1];
-                        //unsigned char r = in[2 * i + 1];
-                        //unsigned char g = in[2 * i + 1];
-                        //unsigned char b = in[2 * i + 1];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        if (in[i] >= infoIn->palettesize) return 46;
+                        j = in[i++] << 2;
+
+                        px_rgb.red = *(infoIn->palette + j);
+                        px_rgb.green = *(infoIn->palette + j + 1);
+                        px_rgb.blue = *(infoIn->palette + j + 2);
+                        *pixel++ = brightness(px_rgb);
                     }
+                    output_row_8(y,(void *)(line_buf),&ctx);
+                }
+                break;
+            case PNG_COLORTYPE_GREYA: /*greyscale with alpha*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        value = in[i++ << 1];
+                        px_rgb.red = (unsigned char)value;
+                        px_rgb.green = (unsigned char)value;
+                        px_rgb.blue = (unsigned char)value;
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
+                }
+                break;
+            case PNG_COLORTYPE_RGBA: /*RGB with alpha*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        j = i++ << 2;
+
+                        px_rgb.red = *(in + j);
+                        px_rgb.green = *(in + j +1);
+                        px_rgb.blue = *(in + j + 2);
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
             default:
-                return 31;
+                break;
             }
         }
         else if (infoIn->bitDepth == 16)
         {
             switch (infoIn->colorType)
             {
-            case 0: /*greyscale color*/
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        value = in[i++ << 1];
+                        px_rgb.red = (unsigned char)value;
+                        px_rgb.green = (unsigned char)value;
+                        px_rgb.blue = (unsigned char)value;
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
+                }
+                break;
+            case PNG_COLORTYPE_RGB: /*RGB color*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i << 1];
-                        //unsigned char r = in[2 * i];
-                        //unsigned char g = in[2 * i];
-                        //unsigned char b = in[2 * i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        j = 6 * i++;
+                        px_rgb.red = *(in + j);
+                        px_rgb.green = *(in + j + 2);
+                        px_rgb.blue = *(in + j + 4);
+                        *pixel++ = brightness(px_rgb);
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
-            case 4: /*greyscale with alpha*/
+            case PNG_COLORTYPE_GREYA: /*greyscale with alpha*/
                 i = 0;
                 for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
                     for (x = 0 ; x < w ; x++) {
-                        c = in[i << 2];
-                        //unsigned char r = in[4 * i];
-                        //unsigned char g = in[4 * i];
-                        //unsigned char b = in[4 * i];
-                        out[i++] = LCD_RGBPACK(c,c,c);
+                        value = in[i++ << 2];
+                        px_rgb.red = (unsigned char)value;
+                        px_rgb.green = (unsigned char)value;
+                        px_rgb.blue = (unsigned char)value;
+                        *pixel++ = brightness(px_rgb);
                     }
+                    output_row_8(y,(void *)line_buf,&ctx);
+                }
+                break;
+            case PNG_COLORTYPE_RGBA: /*RGB with alpha*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        j = i++ << 3;
+                        px_rgb.red = *(in + j);
+                        px_rgb.green = *(in + j + 2);
+                        px_rgb.blue = *(in + j + 4);
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
                 break;
             default:
-                return 31;
+                break;
             }
         }
         else /*infoIn->bitDepth is less than 8 bit per channel*/
         {
-            if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
-            i = 0;
-            for (y = 0 ; y < h ; y++) {
-                for (x = 0 ; x < w ; x++) {
-                    unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
-                    value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
-                    unsigned char r = (unsigned char)value;
-                    unsigned char g = (unsigned char)value;
-                    unsigned char b = (unsigned char)value;
-                    out[i++] = LCD_RGBPACK(r,g,b);
+            switch (infoIn->colorType)
+            {
+            case PNG_COLORTYPE_GREY: /*greyscale color*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
+                        value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
+                        px_rgb.red = (unsigned char)value;
+                        px_rgb.green = (unsigned char)value;
+                        px_rgb.blue = (unsigned char)value;
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
                 }
+                break;
+            case PNG_COLORTYPE_PALETTE: /*indexed color (palette)*/
+                i = 0;
+                for (y = 0 ; y < h ; y++) {
+                    pixel = line_buf;
+                    for (x = 0 ; x < w ; x++) {
+                        value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
+                        if (value >= infoIn->palettesize) return 47;
+                        j = value << 2;
+                        px_rgb.red = infoIn->palette[j];
+                        px_rgb.green = infoIn->palette[j + 1];
+                        px_rgb.blue = infoIn->palette[j + 2];
+                        *pixel++ = brightness(px_rgb);
+                    }
+                    output_row_8(y,(void *)line_buf,&ctx);
+                }
+                break;
+            default:
+                break;
             }
         }
-    }
-    else return 59;
-
+#endif
     return 0;
 }
 
@@ -834,21 +1038,21 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
     size_t i;
     switch (filterType)
     {
-    case 0:
+    case PNG_FILTERTYPE_NONE:
         //for(i = 0; i < length; i++) recon[i] = scanline[i];
         memcpy(recon, scanline, length * sizeof(unsigned char));
         break;
-    case 1:
+    case PNG_FILTERTYPE_SUB:
         //for(i =         0; i < bytewidth; i++) recon[i] = scanline[i];
         memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
         for (i = bytewidth; i <    length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
         break;
-    case 2:
+    case PNG_FILTERTYPE_UP:
         if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
         else       //for(i = 0; i < length; i++) recon[i] = scanline[i];
             memcpy(recon, scanline, length * sizeof(unsigned char));
         break;
-    case 3:
+    case PNG_FILTERTYPE_AVERAGE:
         if (precon)
         {
             for (i =         0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
@@ -861,7 +1065,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
             for (i = bytewidth; i <    length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2;
         }
         break;
-    case 4:
+    case PNG_FILTERTYPE_PAETH:
         if (precon)
         {
             for (i =         0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0));
@@ -1051,7 +1255,7 @@ static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t si
     /*provide some proper output values if error will happen*/
     decoded_image_size = 0;
 
-    if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
+    if (size == 0 || in == NULL) { decoder->error = 48; return; } /*the given data is empty*/
 
     LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/
     if (decoder->error) return;
@@ -1063,13 +1267,21 @@ static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t si
         unsigned chunkLength;
         const unsigned char* data; /*the data in the chunk*/
 
+        /* minimal size of chunk is 12 bytes */
         if ((size_t)((chunk - in) + 12) > size || chunk < in) { decoder->error = 30; break; } /*error: size of the in buffer too small to contain next chunk*/
         chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
+        /* data field of the chunk is restricted to 2^31-1 bytes in size */
         if (chunkLength > 2147483647) { decoder->error = 63; break; }
+
+        /* check if chunk fits in buffer */
         if ((size_t)((chunk - in) + chunkLength + 12) > size || (chunk + chunkLength + 12) < in) { decoder->error = 35; break; } /*error: size of the in buffer too small to contain next chunk*/
         data = LodePNG_chunk_data_const(chunk);
 
         /*IDAT chunk, containing compressed image data*/
+        /* there may be more than 1 IDAT chunk, complete
+         * compressed stream is concatenation of consecutive
+         * chunks data
+         */
         if (LodePNG_chunk_type_equals(chunk, "IDAT"))
         {
             size_t oldsize = idat_size;
@@ -1091,10 +1303,10 @@ static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t si
             if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/
             for (i = 0; i < decoder->infoPng.color.palettesize; i++)
             {
-                decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/
-                decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/
-                decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/
-                decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/
+                decoder->infoPng.color.palette[(i<<2)] = data[pos++]; /*R*/
+                decoder->infoPng.color.palette[(i<<2) | 1] = data[pos++]; /*G*/
+                decoder->infoPng.color.palette[(i<<2) | 2] = data[pos++]; /*B*/
+                decoder->infoPng.color.palette[(i<<2) | 3] = 255; /*alpha*/
             }
             critical_pos = 2;
         }
@@ -1104,68 +1316,76 @@ static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t si
             if (decoder->infoPng.color.colorType == 3)
             {
                 if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/
-                for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i];
+                for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[(i<<2) | 3] = data[i];
             }
-            else if (decoder->infoPng.color.colorType == 0)
+            else if (decoder->infoPng.color.colorType == PNG_COLORTYPE_GREY)
             {
                 if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
                 decoder->infoPng.color.key_defined = 1;
-                decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1];
+                decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = (data[0]<<8) | data[1];
             }
-            else if (decoder->infoPng.color.colorType == 2)
+            else if (decoder->infoPng.color.colorType == PNG_COLORTYPE_RGB)
             {
                 if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
                 decoder->infoPng.color.key_defined = 1;
-                decoder->infoPng.color.key_r = 256 * data[0] + data[1];
-                decoder->infoPng.color.key_g = 256 * data[2] + data[3];
-                decoder->infoPng.color.key_b = 256 * data[4] + data[5];
+                decoder->infoPng.color.key_r = (data[0]<<8) | data[1];
+                decoder->infoPng.color.key_g = (data[2]<<8) | data[3];
+                decoder->infoPng.color.key_b = (data[4]<<8) | data[5];
             }
             else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/
         }
         /*background color chunk (bKGD)*/
         else if (LodePNG_chunk_type_equals(chunk, "bKGD"))
         {
-            if (decoder->infoPng.color.colorType == 3)
+            if (decoder->infoPng.color.colorType == PNG_COLORTYPE_PALETTE)
             {
                 if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
                 decoder->infoPng.background_defined = 1;
-                decoder->infoPng.background_r = decoder->infoPng.color.palette[4 * data[0] + 0];
-                decoder->infoPng.background_g = decoder->infoPng.color.palette[4 * data[0] + 1];
-                decoder->infoPng.background_b = decoder->infoPng.color.palette[4 * data[0] + 2];
+                decoder->infoPng.background_r = decoder->infoPng.color.palette[(data[0]<<2)];
+                decoder->infoPng.background_g = decoder->infoPng.color.palette[(data[0]<<2) | 1];
+                decoder->infoPng.background_b = decoder->infoPng.color.palette[(data[0]<<2) | 2];
 
             }
-            else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4)
+            else if (decoder->infoPng.color.colorType == PNG_COLORTYPE_GREY || 
+                     decoder->infoPng.color.colorType == PNG_COLORTYPE_GREYA)
             {
                 if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
                 decoder->infoPng.background_defined = 1;
-                decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1];
+                decoder->infoPng.background_r = 
+                    decoder->infoPng.background_g = 
+                        decoder->infoPng.background_b = (data[0]<<8) | data[1];
             }
-            else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6)
+            else if (decoder->infoPng.color.colorType == PNG_COLORTYPE_RGB || 
+                     decoder->infoPng.color.colorType == PNG_COLORTYPE_RGBA)
             {
                 if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
                 decoder->infoPng.background_defined = 1;
-                decoder->infoPng.background_r = 256 * data[0] + data[1];
-                decoder->infoPng.background_g = 256 * data[2] + data[3];
-                decoder->infoPng.background_b = 256 * data[4] + data[5];
+                decoder->infoPng.background_r = (data[0]<<8) | data[1];
+                decoder->infoPng.background_g = (data[2]<<8) | data[3];
+                decoder->infoPng.background_b = (data[4]<<8) | data[5];
             }
         }
+#if 0
         else if (LodePNG_chunk_type_equals(chunk, "tIME"))
         {
             if (chunkLength != 7) { decoder->error = 73; break; }
             decoder->infoPng.time_defined = 1;
-            decoder->infoPng.time.year = 256 * data[0] + data[+ 1];
+            decoder->infoPng.time.year = (data[0]<<8) | data[1];
             decoder->infoPng.time.month = data[2];
             decoder->infoPng.time.day = data[3];
             decoder->infoPng.time.hour = data[4];
             decoder->infoPng.time.minute = data[5];
             decoder->infoPng.time.second = data[6];
         }
+#endif
         else if (LodePNG_chunk_type_equals(chunk, "pHYs"))
         {
             if (chunkLength != 9) { decoder->error = 74; break; }
             decoder->infoPng.phys_defined = 1;
-            decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3];
-            decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7];
+            decoder->infoPng.phys_x = (data[0]<<24) | (data[1]<<16) |
+                                      (data[2]<<8) | data[3];
+            decoder->infoPng.phys_y = (data[4]<<24) | (data[5]<<16) |
+                                      (data[6]<<8) | data[7];
             decoder->infoPng.phys_unit = data[8];
         }
         else /*it's not an implemented chunk type, so ignore it: skip over the data*/
@@ -1195,6 +1415,7 @@ static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t si
 
         if (!decoder->error)
         {
+            /* size of decoded image in bytes rounded up */
             decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8;
             if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; }
             decoded_image = memory_max - decoded_image_size;
@@ -1215,12 +1436,13 @@ void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize,
     decodeGeneric(decoder, in, insize, pf_progress);
     if (decoder->error) return;
 
-    /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/
-    if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; }
     converted_image = (fb_data *)memory;
-    converted_image_size = decoder->infoPng.width*decoder->infoPng.height;
+
+    /* allocate one more line for pixel format transformation */
+    converted_image_size = decoder->infoPng.width * (decoder->infoPng.height+1) * FB_DATA_SZ;
+
     if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; }
-    if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
+    if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
 }
 
 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings)
@@ -1310,10 +1532,18 @@ void draw_image_rect(struct image_info *info,
                      int x, int y, int width, int height)
 {
     fb_data **pdisp = (fb_data**)info->data;
+
+#ifdef HAVE_LCD_COLOR
     rb->lcd_bitmap_part(*pdisp, info->x + x, info->y + y, info->width,
                         x + MAX(0, (LCD_WIDTH-info->width)/2),
                         y + MAX(0, (LCD_HEIGHT-info->height)/2),
                         width, height);
+#else
+    mylcd_ub_gray_bitmap_part(*pdisp, info->x + x, info->y + y, info->width,
+                              x + MAX(0, (LCD_WIDTH-info->width)/2),
+                              y + MAX(0, (LCD_HEIGHT-info->height)/2),
+                              width, height);
+#endif
 }
 
 int img_mem(int ds)
@@ -1379,7 +1609,7 @@ int load_image(char *filename, struct image_info *info,
 #endif
 
         decoder->settings.color_convert = 1;
-        decoder->infoRaw.color.colorType = 2;
+        decoder->infoRaw.color.colorType = PNG_COLORTYPE_RGB;
         decoder->infoRaw.color.bitDepth = 8;
 
         LodePNG_inspect(decoder, image, image_size);
@@ -1504,10 +1734,10 @@ int get_image(struct image_info *info, int ds)
         bmp_dst.data = (unsigned char *)*p_disp;
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
         rb->cpu_boost(true);
-        smooth_resize_bitmap(&bmp_src, &bmp_dst);
+        resize_bitmap(&bmp_src, &bmp_dst);
         rb->cpu_boost(false);
 #else
-        smooth_resize_bitmap(&bmp_src, &bmp_dst);
+        resize_bitmap(&bmp_src, &bmp_dst);
 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
     } else {
         *p_disp = converted_image;
diff --git a/apps/plugins/imageviewer/png/png.h b/apps/plugins/imageviewer/png/png.h
index e351d9a..6755617 100644
--- a/apps/plugins/imageviewer/png/png.h
+++ b/apps/plugins/imageviewer/png/png.h
@@ -27,3 +27,16 @@
 #define OUT_OF_MEMORY   9900
 #define FILE_TOO_LARGE  9910
 
+/* PNG color types */
+#define PNG_COLORTYPE_GREY    0
+#define PNG_COLORTYPE_RGB     2
+#define PNG_COLORTYPE_PALETTE 3
+#define PNG_COLORTYPE_GREYA   4
+#define PNG_COLORTYPE_RGBA    6
+
+/* PNG filter types */
+#define PNG_FILTERTYPE_NONE    0
+#define PNG_FILTERTYPE_SUB     1
+#define PNG_FILTERTYPE_UP      2
+#define PNG_FILTERTYPE_AVERAGE 3
+#define PNG_FILTERTYPE_PAETH   4
diff --git a/apps/plugins/imageviewer/png/png.make b/apps/plugins/imageviewer/png/png.make
index b2000fa..6e3cea6 100644
--- a/apps/plugins/imageviewer/png/png.make
+++ b/apps/plugins/imageviewer/png/png.make
@@ -19,7 +19,7 @@ PNG_OBJ := $(call c2obj, $(PNG_SRC))
 OTHER_SRC += $(PNG_SRC)
 
 # Use -O3 for png plugin : it gives a bigger file but very good performances
-PNGFLAGS = $(PLUGINFLAGS) -O3 -DNO_GZCOMPRESS -DNO_GZIP
+PNGFLAGS = $(PLUGINFLAGS) -Os -DNO_GZCOMPRESS -DNO_GZIP
 
 $(PNGBUILDDIR)/png.rock: $(PNG_OBJ)
 
