Страница 1 из 1

перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Сб авг 13, 2011 21:21:15
demonchik
всем Котам МЯУ!!! вот получилось у мну работать с картой памяти(ATmega32, LCD LS020). решил вывести на дисплей рисунок BMP. описание нашел, откуда начинать считывать - нашел. http://jenyay.net/Programming/Bmp но вот надо преобразовать 24 бита в 16 формата 5-6-5. прошу кинуть в меня ссылкой, алгоритмом. если кинете в меня код - не обижусь)))

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Сб авг 13, 2011 21:48:27
Satyr
#define RGB24TOLCD(val) (((val & 0x00f80000) >> 8 ) | ((val & 0x0000fc00) >> 5) |((val & 0x000000f8) >> 3))

Это если экран у тебя RGB берет. Еще BGR популярно :))

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Сб авг 13, 2011 21:53:39
demonchik
:)) спрасиб огромное!!!!!!!!!!! вот сделал чтение текстовых файлов, захотелось и изображений тоже. правда на расжатие jpeg уже памяти МК не хватает, а на BMP еще попробую)))

Добавлено: Вс авг 14, 2011 10:28:15
avreal
«Ну вот опять»™
Приблизительно как в теме про преобразование текста в число
Такой #define выглядит красиво, но работает далеко от оптимального. Двигает слишком много.

Вот полный функциональный аналог макроса. Почти не думая записанный не в одну строку, зато с учетом того, что часть сдвигов полезна для других:

Код: Выделить всё

#include <stdint.h>

#define RGB24TOLCD(val) (((val & 0x00f80000) >> 8 ) | ((val & 0x0000fc00) >> 5) |((val & 0x000000f8) >> 3))

uint16_t rgb24to16(uint32_t rgb24)
{
        return RGB24TOLCD(rgb24);
}

uint16_t rgb24to16a(uint32_t rgb24)
{
        uint16_t rgb16;
        rgb24 >>= 3;
        rgb16 = rgb24 & 0x1F;
        rgb24 >>= 2;
        rgb16 |= rgb24 & (0x3F << 5);
        rgb24 >>= 3;
        rgb16 |= rgb24 & (0x1F << 11);
        return rgb16;
}
avr-gcc -Os -c -Wa,-al=rgb.txt -mmcu=atmega32 rgb.c
Итого первая функция занимает 102 байта, вторая -- 66. И время выполнения будет приблизительно в два раза меньше (около полусотни тактов вместо около сотни). Это при компиляции на размер. Если на скорость (-O2), то размер и время первой функции не меняются. Вторая становится на два байта длиннее, зато время выполнения тактов на шесть сокращается (второй сдвиг делается не циклом на два прохода, а линейным кодом).
Листинг компилятора цепляю.
rgb.txt
(3.8 КБ) 189 скачиваний

Хотя, конечно, как раз в этом месте аккуратная ассемблерная вставочка не помешала бы. Как умеет gcc -- не ломая оптимизацию С-шного кода. Причём если в ней объединить и вывод на SPI, то преобразование вообще не будет занимать времени, будет делаться во время вывода предыдущего пиксела. Так на глаз, без написания кода, это тактов пятнадцать, не больше.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 12:00:34
demonchik
avreal, и все кто знает - вопрос))) есть у меня буфер обьявленный как

Код: Выделить всё

unsigned char buf[512];

допустим там лежат байты

Код: Выделить всё

{0x00, 0x00, 0x00, ... , 0x00}


для хранения пикселя объявляю 32-разрядную переменную

Код: Выделить всё

unsigned long data;

тут будет {0x00000000} либо {00000000.00000000.00000000.00000000} //вроде посчитал 32)))
нужно buf[0] записать в 3-й "байт" data, buf[1] во 2-й "байт" data и buf[1] - в первый)))

правильно ли я составил конструкцию которая эту чтуку реализует?

Код: Выделить всё

data=((buf[0]<<15)|(buf[1]<<7)|(buf[2]<<0));


правильно ли я понимаю свою же задачу?

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 12:02:32
demonchik
потом data пихать так думаю в uint16_t rgb24to16a(uint32_t rgb24)

Код: Выделить всё

rgb24to16a(data);

Re:

Добавлено: Вс авг 14, 2011 12:06:07
Satyr
avreal писал(а):Такой #define выглядит красиво, но работает далеко от оптимального. Двигает слишком много.

Смотря на чем. Это под 32 бит процессор.
Под 8 битном, возможно, чисто на оптимизатор компилятора надеяться не стоит.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 12:13:49
Satyr
Под 8бит целесообразней функцию вобще на манипуляцию над отдельными байтами переложиь, даюе без int16

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 13:10:41
demonchik
и вот еще интьересно - МК не будет делать смещения в file_1.buf[i]? - ведь тогда будет переполнение... годится ли такая конструкция для записи в дата со смещением без изменения file_1.buf[]?

Код: Выделить всё

 uint32_t data=(((file_1.buf[0])<<15)|((file_1.buf[1])<<7)|((file_1.buf[2])<<0));

Re: Re:

Добавлено: Вс авг 14, 2011 14:07:56
avreal
Satyr писал(а):
avreal писал(а):Такой #define выглядит красиво, но работает далеко от оптимального. Двигает слишком много.
Смотря на чем. Это под 32 бит процессор.
Хоть и там, и там есть буквы «32», всё же не оно :)))
demonchik писал(а):всем Котам МЯУ!!! вот получилось у мну работать с картой памяти(ATmega32, LCD LS020).
Для АРМ-а, у которого сдвиги практически "на шару" -- согласен, тот #define неплох.

Satyr писал(а):Под 8 битном, возможно, чисто на оптимизатор компилятора надеяться не стоит.
Да нужно просто правильно объяснить ему, что нужно сделать с данными. Хотя тут правильнее будет выписать на асме преобразование вместе с выводом в SPI. Маленькая функция, которая всё сильно ускорит.

Satyr писал(а):Под 8бит целесообразней функцию вобще на манипуляцию над отдельными байтами переложиь, даюе без int16
Пробовал и для входа, и для выхода union из uint32_t / uint16_t и соответствующих байтовых массивов. Чтобы манипулировать байтами, а заносить в union из входного 32-битного и возвращать из union из 16-битного. Оказалось даже слегка хуже, чем вторая функция.
Думаю, лучше в цикле по пикселам побайтно по указателю вынимать отдельные цвета и нужное форимровать. Но это «вообще не тот #define», а я хотел сравнить именно «красивую» запись и ориентированную на 8-битник.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 14:35:33
avreal
demonchik писал(а):для хранения пикселя объявляю 32-разрядную переменную

Код: Выделить всё

unsigned long data;
тут будет {0x00000000} либо {00000000.00000000.00000000.00000000} //вроде посчитал 32)))
нужно buf[0] записать в 3-й "байт" data, buf[1] во 2-й "байт" data и buf[1] - в первый)))
правильно ли я составил конструкцию которая эту чтуку реализует?

Код: Выделить всё

data=((buf[0]<<15)|(buf[1]<<7)|(buf[2]<<0));
См. выше -- лучше вообще указателем побайтно идти. Просто зная, что байты лежат в таком-то порядке.

Код: Выделить всё

    uint8_t *ptr = buf;
    for( идём по пикселам ) {
        // смещениями к указателю выбираем RGB
        uint16_t rgb565 = ((ptr[0] & 0xF8) << 8) | (ptr[1] >> 3); //
        rgb565 |= (ptr[1] & 0xFC) << 3; // G
        // выводим
        // смещаем указатель на 3 байта
        ptr += 3;
    }
Никакие промежуточные 32-битные переменные не нужны. Только зря туда-сюда ползать.
Красивее это можно записать так:

Код: Выделить всё

    typedef struct {
        uint8_t r, g, b; // или какой там порядок в буфере
    } rgb888_t;
    rgb888_t *pixels = (rgb888_t*)buf; // мапим массив структур на массив байтов
    for( идём по пикселам ) {
        // смещениями к указателю выбираем RGB
        // я не знаю, какой порядок на входе и на выходе, это так, намёк
        // 16-битная переменная тут и для AVR может оказаться лучше, так как сдвиг на 8
        // с большой вероятностью компилятор превратит в один mov, а вот зелёный, который
        // разносится на два байта, может обработаться лучше.
        uint16_t rgb565 = ((pixels->r & 0xF8) << 8) | (pixels->b >> 3);
        rgb565 |= (pixels->g & 0xFC) << 3;
        // выводим
        // смещаем указатель на размер структуры, переходим к следующему пикселу
        ++pixels;
    }
Код на AVR должен получиться такой же.
Недостаток -- на чём-то тольще 8-битника нужно думать про упаковку структур, компилятор не должен добавлять лишних байтов в структуру для выравнивания на слово. Платформо-зависимо, так что лучше врукопашную по байтам идти.
Ну и если думать про оптимальность, то лучше rgb565 объявить как uint_fast16_t, тогда на том же ARM-е будет заведена 32-битная переменная и построен код получше.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 14:43:48
avreal
Думаю, лучше всего основной цикл реализовать как

Код: Выделить всё

    uint8_t *ptr = buf;
    for( по пикселам ) {
        out_to_lcd( ptr[2], ptr[1], ptr[0]); // или какой там порядок
        ptr += 3;
    }
Функция void out_to_lcd(uint8_t r, uint8_t g, uint8_t b); должна сразу и сдвигать и выводить в индикатор. Плюсы:
1) При смене индикатора на имеющий цвет 8/8/8 эта часть не меняется.
2) При работе через SPI можно подготовить первый байт, послать, потом двигать/маскировать данные для второго байта и послать его. Еще и ожидания готовности SPI ставить перед выводом уже готового байта. Сильно ускорит процесс.

Поначалу функцию написать «как понятно», чтобы убедиться, что все работает.
Потом уже оптимизировать, inline от C99 поможет, а может, ассемблерную вставку в ней сделать -- это по месту смотреть.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 14:55:03
demonchik
спасиб, хлопцы за направление на путь истинный)))

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 16:33:59
demonchik
наверно так
Код:

Код: Выделить всё

    uint8_t *ptr = buf;
    for( идём по пикселам ) {
        // смещениями к указателю выбираем RGB
        uint16_t rgb565 = ((ptr[0] & 0xF8) << 8) | ((ptr[2]&0xF8) >> 3); //
        rgb565 |= (ptr[1] & 0xFC) << 3; // G
        // выводим
        // смещаем указатель на 3 байта
        ptr += 3;
    }

действительно, расписав все по битам понял что к чему. спасибо!!!

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вс авг 14, 2011 22:34:39
avreal
demonchik писал(а):

Код: Выделить всё

        uint16_t rgb565 = ((ptr[0] & 0xF8) << 8) | ((ptr[2]&0xF8) >> 3);

Для ptr[2] >> 3 маскирование не нужно, при сдвиге вправо эти биты всё равно уйдут за край.
Главное, чтобы слева не вдвинулось чего не надо, но это гарантируется для беззнакового числа (заполнение нулями слева при сдвиге вправо). Так что (ptr[2] >> 3) & 0x1F тоже не нужно.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Пн авг 15, 2011 23:06:30
demonchik
наваял я такую чтуку

Код: Выделить всё

void BMP_show(unsigned char x, unsigned char y, unsigned char w, unsigned char h)
{
int i;
uint16_t rgb565;
uint8_t *ptr=&file_1.buf[54];

 lcd_c(0xEF90);
 lcd_c(0x0504);
 lcd_c(0x0800+y);
 lcd_c(0x0900+y+h-1);
 lcd_c(0x0A00+DISP_H-x-w);
 lcd_c(0x0B00+DISP_H-x-1);
 
if ((res=f_open(&file_1,"0:/AMANDA.BMP",FA_READ)) == FR_OK)
 do
 {
 res=f_read(&file_1,file_1.buf,sizeof(file_1.buf)-1,&nbytes);   
     for(i=0;i<(nbytes/3);i++)
    {
 
        rgb565 = (((ptr[2] & 0xF8) << 8) | ((ptr[0] & 0xF8) >> 3)); //   R B
        rgb565 |= (ptr[1] & 0xFC) << 3; // G
    //имеем rgb565 формата RRRRRGGGGGGBBBBB   5-6-5
    // выводим
    lcd_d(rgb565);

    // смещаем указатель на 3 байта
        ptr += 3;
    }   
   
   
 }while(res==FR_OK/*|| nbytes == sizeof(file_1.buf)*/);

но выводит только первую строку изображения(даже верно). по коду смотрю - вроде норма(( не могу понять своего затыка.

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Пн авг 15, 2011 23:14:29
demonchik
заметил что в

Код: Выделить всё

for(i=0;i<([color=#FF4040]nbytes/3[/color]);i++) 

nbytes/3 - это только в случае если не первая итерация цикла.... в первой итерации

Код: Выделить всё

(nbytes-ofset)/3
- тогда число может быть не целое. кажется изображение будет искаженным....

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Пн авг 15, 2011 23:22:48
demonchik
и с do while я намутил)))
- работает)) только бы сдвиг байт бы устранить)) а так весь дисплей заливает)))

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Вт авг 16, 2011 20:18:03
demonchik
свершилось чудо!!!

Re: перевод 24 разрядного изображения в 16 (5-6-5)

Добавлено: Чт авг 25, 2011 09:58:27
demonchik
нашел тему - http://www.circuitidea.com/Article/DIY- ... 0PLUS.html
тут достаточно инфы, чтоб все получилось (по примеру делал)