Subversion Repositories freemyipod

Rev

Blame | Last modification | View Log | RSS feed

//
//
//    Copyright 2011 TheSeven
//
//
//    This file is part of emCORE.
//
//    emCORE is free software: you can redistribute it and/or
//    modify it under the terms of the GNU General Public License as
//    published by the Free Software Foundation, either version 2 of the
//    License, or (at your option) any later version.
//
//    emCORE is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//    See the GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License along
//    with emCORE.  If not, see <http://www.gnu.org/licenses/>.
//
//


#include "emcorelib.h"
#include "dither.h"


static void dither_slow(int width, int height, void* inbuf, int inx, int iny, int instride,
                        void* outbuf, int outx, int outy, int outstride)
{
    int bpp = lcd_get_bytes_per_pixel();
    int bits = lcd_get_format();
    int swap = bits & BIT(30);
    int rwidth = MAX(7, bits & BITRANGE(0, 3));
    int gwidth = MAX(7, bits & BITRANGE(10, 13));
    int bwidth = MAX(7, bits & BITRANGE(20, 23));
    int rshift = bits & BITRANGE(4, 9);
    int gshift = bits & BITRANGE(14, 19);
    int bshift = bits & BITRANGE(24, 29);
    int roffs = 1 << (6 - rwidth);
    int goffs = 1 << (6 - gwidth);
    int boffs = 1 << (6 - bwidth);
    int rmask = BITRANGE(7 - rwidth, 7);
    int gmask = BITRANGE(7 - gwidth, 7);
    int bmask = BITRANGE(7 - bwidth, 7);
    int rclip = 7 - rshift;
    int gclip = 7 - gshift;
    int bclip = 7 - bshift;
    char* in = ((char*)inbuf) + (instride * iny + inx) * 3;
    char* out = ((char*)outbuf) + (instride * iny + inx) * bpp;
    int x, y;
    for (y = 0; y < height; y++)
    {
        for (x = 0; x < width; x++)
        {
            int origb = *in++;
            int origg = *in++;
            int origr = *in++;
            int realr = origr >> rclip;
            int realg = origg >> gclip;
            int realb = origb >> bclip;
            int errr = origr - (realr << rclip) - roffs;
            int errg = origg - (realg << gclip) - goffs;
            int errb = origb - (realb << bclip) - boffs;
            if (x + 1 < width)
            {
                *(in + 0) = MAX(0, MIN(255, *(in + 0) + errb / 2));
                *(in + 1) = MAX(0, MIN(255, *(in + 1) + errg / 2));
                *(in + 2) = MAX(0, MIN(255, *(in + 2) + errr / 2));
            }
            if (y + 1 < height)
            {
                *(in + 3 * instride - 1) = MAX(0, MIN(255, *(in + 3 * instride - 1) + errr / 4));
                *(in + 3 * instride - 2) = MAX(0, MIN(255, *(in + 3 * instride - 2) + errg / 4));
                *(in + 3 * instride - 3) = MAX(0, MIN(255, *(in + 3 * instride - 3) + errb / 4));
                *(in + 3 * instride - 4) = MAX(0, MIN(255, *(in + 3 * instride - 4) + errr / 4));
                *(in + 3 * instride - 5) = MAX(0, MIN(255, *(in + 3 * instride - 5) + errg / 4));
                *(in + 3 * instride - 6) = MAX(0, MIN(255, *(in + 3 * instride - 6) + errb / 4));
            }
            int pixel = (realr << rshift) | (realg << gshift) | (realb << bshift);
            if (bpp == 1) *out = pixel;
            else if (bpp == 2)
            {
                if (swap) *((short*)out) = (pixel >> 8) | ((pixel << 8) & 0xff00);
                else *((short*)out) = pixel;
            }
            else if (bpp == 3)
            {
                if (swap)
                {
                    *(out + 0) = pixel & 0xff;
                    *(out + 1) = (pixel >> 8) & 0xff;
                    *(out + 2) = (pixel >> 16) & 0xff;
                }
                else
                {
                    *(out + 0) = (pixel >> 16) & 0xff;
                    *(out + 1) = (pixel >> 8) & 0xff;
                    *(out + 2) = pixel & 0xff;
                }
            }
            else if (bpp == 4)
            {
                if (swap) *((int*)out) = (pixel >> 24) | ((pixel >> 8) & 0xff00)
                                       | ((pixel << 8) & 0xff0000) | ((pixel << 24) & 0xff000000);
                else *((int*)out) = pixel;
            }
            out += bpp;
        }
        in += (instride - width) * 3;
        out += (outstride - width) * bpp;
    }
}

static void dither_rgb565(int width, int height, void* inbuf, int inx, int iny, int instride,
                          void* outbuf, int outx, int outy, int outstride)
{
    char* in = ((char*)inbuf) + (instride * iny + inx) * 3;
    short* out = ((short*)outbuf) + instride * iny + inx;
    int x, y;
    for (y = 0; y < height; y++)
    {
        for (x = 0; x < width; x++)
        {
            int origb = *in++;
            int origg = *in++;
            int origr = *in++;
            int realr = origr >> 3;
            int realg = origg >> 2;
            int realb = origb >> 3;
            int errr = origr - (realr << 3) - 4;
            int errg = origg - (realg << 2) - 2;
            int errb = origb - (realb << 3) - 4;
            if (x + 1 < width)
            {
                *(in + 0) = MAX(0, MIN(255, *(in + 0) + errb / 2));
                *(in + 1) = MAX(0, MIN(255, *(in + 1) + errg / 2));
                *(in + 2) = MAX(0, MIN(255, *(in + 2) + errr / 2));
            }
            if (y + 1 < height)
            {
                *(in + 3 * instride - 1) = MAX(0, MIN(255, *(in + 3 * instride - 1) + errr / 4));
                *(in + 3 * instride - 2) = MAX(0, MIN(255, *(in + 3 * instride - 2) + errg / 4));
                *(in + 3 * instride - 3) = MAX(0, MIN(255, *(in + 3 * instride - 3) + errb / 4));
                *(in + 3 * instride - 4) = MAX(0, MIN(255, *(in + 3 * instride - 4) + errr / 4));
                *(in + 3 * instride - 5) = MAX(0, MIN(255, *(in + 3 * instride - 5) + errg / 4));
                *(in + 3 * instride - 6) = MAX(0, MIN(255, *(in + 3 * instride - 6) + errb / 4));
            }
            *out++ = (realr << 11) | (realg << 5) | realb;
        }
        in += (instride - width) * 3;
        out += outstride - width;
    }
}


void dither(int width, int height, void* inbuf, int inx, int iny, int instride,
            void* outbuf, int outx, int outy, int outstride)
{
    int bits = lcd_get_format();
    if (bits == 0x004154b1)
        dither_rgb565(width, height, inbuf, inx, iny, instride, outbuf, outx, outy, outstride);
    else dither_slow(width, height, inbuf, inx, iny, instride, outbuf, outx, outy, outstride);
}