ListView, ImageList, Icons 16-bit

 
0
 
C++
ava
Alca | 21.01.2013, 02:51
Как-то криво отображаются иконки при 16-битном системном цвете. Как пофиксить?
Кода много, поэтому выложу пока это, если необходим какой-то конкретный кусок,
то могу дописать.


//  ImageList
list_images = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR16, 40, 80);


/*
* We use 32bpp images for menu. After these images are loaded from resources
* the RGB values should be multiple to alpha to allow system to show ARGB
* bitmaps correctly.
*/
HBITMAP
pre_multiple_rgb(
    HBITMAP hsrc,
    WORD    wBitsPixel
)
{
    HBITMAP hold = NULL;
    HDC hdcsrc = NULL;
    HDC hdc = GetDC(m_hMainDlg);
    BITMAPINFOHEADER bi;
    BITMAP bmp;
    BITMAPINFO bmi;
    HBITMAP dib = NULL;;
    UCHAR *dst = NULL;
    UCHAR *src, *tmp;
    UCHAR alpha;
    int bpl;
    ULONG x, y;
    DWORD bmp_size = 0;
    HANDLE hdib = NULL;
    char *lpbitmap = NULL;

    hdcsrc = CreateCompatibleDC(hdc);
    hold   = (HBITMAP)SelectObject(hdcsrc, hsrc);
    
    GetObject(hsrc, sizeof(BITMAP), &bmp);

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmp.bmWidth;;
    bi.biHeight = bmp.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = wBitsPixel;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    bmp_size = ((bmp.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmp.bmHeight;
    hdib = GlobalAlloc(GHND, bmp_size);

    lpbitmap = (char *)GlobalLock(hdib);

    /* Get bits of source bitmap. */
    GetDIBits(hdcsrc, hsrc, 0, bmp.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS))

    /* For destination bitmap */
    bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
    bmi.bmiHeader.biWidth = bmp.bmWidth;
    bmi.bmiHeader.biHeight = bmp.bmHeight;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = wBitsPixel;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biSizeImage = 0;
    bmi.bmiHeader.biXPelsPerMeter = 0;
    bmi.bmiHeader.biYPelsPerMeter = 0;
    bmi.bmiHeader.biClrUsed = 0;
    bmi.bmiHeader.biClrImportant = 0;

    dib = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&dst, NULL,    0);

    bpl = 4 * bmp.bmWidth; /* bytes per line */
    src = (UCHAR*)lpbitmap;
    for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
        tmp = src;
        for (x = 0; x < bmp.bmWidth; x++) {
            alpha = tmp[3];

            dst[0] = tmp[0] * alpha / 255;
            dst[1] = tmp[1] * alpha / 255;
            dst[2] = tmp[2] * alpha / 255;
            dst[3] = alpha;

            dst += 4;
            tmp += 4;
        }
    }

stop_rgb:
    SelectObject(hdcsrc, hold);
    DeleteDC(hdcsrc);

    if (hdib) {
        GlobalUnlock(hdib);
        GlobalFree(hdib);
    }

    return dib;
}
Ответы (40)
ava
Alca | 21.01.2013, 04:48 #
ImageList Transparency on Listviews?

Цитата


First up, ImageList_ReplaceIcon copies the icon data when adding it to an image list. So the HICON needs to be released afterwards.



Next, imagelists are natively bitmaps, not icons. And the way you are creating your imagelist makes the conversion of icon to bitmap very ambiguous. ILC_COLOR32 implies the imagelist should be created as a 32bit dib section, which typically contain transparency information via an embedded alpha channel. ILC_MASK instead implies that the internal bitmaps are DDB bitmaps, with the transparency information stored as a 1bpp mask bitmap.



The quickest solution to your problem - take your two icons:

Merge them into a single bitmap resource thats 32 pels wide by 16 high. Fill the background with a mask color :- purple or something.

Create the bitmap using ILC_COLOR|ILC_MASK

Load the bitmap being sure NOT to use LR_TRANSPARENT.

Add the bitmap using ImageList_AddMasked passing in a COLORREF that represents the mask color.



OR, for a better visual effect...

export your PNG data as a 32x16 32bpp bitmap file containing pre-multiplied alpha channel data.

Create the imagelist using the ILC_COLOR32 value.

LoadImage() with LR_CREATEDIBSECTION to load the bitmap as a 32bpp dib section.

Add the image using ImageList_Add()



(the last option is kind of tricky as the number of tools that support writing out 32bit bmp files with properly pre multiplied alpha channels is rather low).







Edited to add the following code sample. Using a 4bpp bitmap created in the dev environment this works just great :-



HWND hwndCtl = CreateWindowEx(0,WC_LISTVIEW,TEXT("ListView1"),WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL,0,0,cx,cy,hWnd,(HMENU)101,hModule,NULL);

HBITMAP hbm = (HBITMAP)LoadImage(hModule,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,0);

COLORREF crMask=RGB(255,0,255);

HIMAGELIST himl = ImageList_Create(16,16,ILC_COLOR|ILC_MASK,2,0);

ImageList_AddMasked(himl,hbm,crMask);

ListView_SetImageList(hwndCtl,himl,LVSIL_NORMAL);



ava
feodorv | 21.01.2013, 07:43 #
Так в чём суть-то? В том. что не нужно ILC_COLOR16, а нужно ILC_COLOR?
ava
artsb | 21.01.2013, 08:41 #
Цитата (Alca @  21.1.2013,  01:51 findReferencedText)
    bpl = 4 * bmp.bmWidth; /* bytes per line */
  src = (UCHAR*)lpbitmap;
  for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
    tmp = src;
    for (x = 0; x < bmp.bmWidth; x++) {
    alpha = tmp[3];
    dst[0] = tmp[0] * alpha / 255;
    dst[1] = tmp[1] * alpha / 255;
    dst[2] = tmp[2] * alpha / 255;
    dst[3] = alpha;
    dst += 4;
    tmp += 4;
    }
  }

Это не правильно, ИМХО. Вы преобразуете 32bit битмап в 16bit битмап. Вы подразучеваете, что 16bit битмапе тоже 4 байта для представления цвета. Но это не так - High color (15/16-bit). Как следует из названия, там всего лишь 2 байта.
ava
Alca | 21.01.2013, 12:20 #
Цитата


Так в чём суть-то? В том. что не нужно ILC_COLOR16, а нужно ILC_COLOR?


нет,  в том, что надо юзать ImageList_AddMasked для прозрачности битмапа

artsb, так, что ли?

        bpl = 2 * bmp.bmWidth; /* bytes per line */
        src = (UCHAR*)lpbitmap;
        for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
            tmp = src;
            for (x = 0; x < bmp.bmWidth; x++) {
                dst[0] = tmp[0];
                dst[1] = tmp[1];

                dst += 2;
                tmp += 2;
            }
        }



Значит, сделал как писал, использовал 2 байта + ImageList_AddMasked.
Получилось!!
Спасибо всем.
ava
artsb | 21.01.2013, 13:36 #
Цитата (Alca @  21.1.2013,  12:20 findReferencedText)
artsb, так, что ли?

Неа.
Судя по Википедии, может быть три варианта:

11111111 11111111
|  ||  | |  ||  |
\  /\  / \  /\  /
r   g    b   a


11111111 11111111
|   ||    ||   ||
\   /\   /  \  /|
  r    g     b  a


11111111 11111111
|   ||     ||   |
\   /\    /  \  /
  r    g       b  

Какой из них используется в Винде я не знаю. Нужно покопаться.

Добавлено позднее:
Или реализовать все и посмотреть, какой из них будет работать  smile 
ava
Alca | 21.01.2013, 13:48 #
У меня так работает (16/32 bit):

    if (16 >= wBitsPixel) {
        bpl = 4 * bmp.bmWidth; /* bytes per line */
        src = (UCHAR*)lpbitmap;
        for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
            tmp = src;
            for (x = 0; x < bmp.bmWidth; x++) {
                dst[0] = tmp[0];
                dst[1] = tmp[1];
                dst[2] = tmp[2];
                dst[3] = tmp[3];

                dst += 4;
                tmp += 4;
            }
        }
    } else {
        bpl = 4 * bmp.bmWidth; /* bytes per line */
        src = (UCHAR*)lpbitmap;
        for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
            tmp = src;
            for (x = 0; x < bmp.bmWidth; x++) {
                alpha = tmp[3];

                dst[0] = tmp[0] * alpha / 255;
                dst[1] = tmp[1] * alpha / 255;
                dst[2] = tmp[2] * alpha / 255;
                dst[3] = alpha;

                dst += 4;
                tmp += 4;
            }
        }
    }
ava
artsb | 21.01.2013, 14:24 #
Цитата (Alca @  21.1.2013,  13:48 findReferencedText)
У меня так работает (16/32 bit):

Странно... Я себе это представлял несколько иначе (для первого случая):

    union UColor16bit
    {
        unsigned short Color; // 16bit
        struct
        {
            unsigned char Hi;
            unsigned char Low;
        } Parts;
        struct
        {
            unsigned short r: 4;
            unsigned short g: 4;
            unsigned short b: 4;
            unsigned short a: 4;
        } ARGB;
    };

    bpl = 4 * bmp.bmWidth; /* bytes per line */
    src = (UCHAR*)lpbitmap;
    UColor16bit cColor;
    for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
        tmp = src;
        for (x = 0; x < bmp.bmWidth; x++) {
            cColor.Color = 0;
            
            cColor.ARGB.r = tmp[0] & 0xF;
            cColor.ARGB.g = tmp[1] & 0xF;
            cColor.ARGB.b = tmp[2] & 0xF;
            cColor.ARGB.a = tmp[3] & 0xF;
            
            dst[0] = cColor.Parts.Hi;
            dst[1] = cColor.Parts.Low;
            
            dst += 2; // 16bit
            tmp += 4;
        }
    }
ava
Alca | 21.01.2013, 14:31 #
Тестил на WinXP, Win2003 Server.
artsb, если честно, то я давно не кодил гуй на чистом WinAPI,
может ты и правильно сдеалал
ava
artsb | 21.01.2013, 14:42 #
Цитата (Alca @  21.1.2013,  14:31 findReferencedText)
Тестил на WinXP, Win2003 Server.

artsb, если честно, то я давно не кодил гуй на чистом WinAPI, 

может ты и правильно сдеалал 

Может быть smile Просто ваш рабочий код рознится с описанием с Вики smile Ну, раз работает... Хотя, я бы разобрался )
ava
Alca | 21.01.2013, 14:50 #
Будет время - надо бы разобраться.
Еще раз, спасибо.
ava
Dem_max | 23.01.2013, 06:45 #
А просто так не пробовал ???

HICON hIcon = (HICON)LoadImage(hModule, szIcon, IMAGE_ICON, cx, cy, LR_CREATEDIBSECTION);
                                                           
himl = ImageList_Create(cx, cy, ILC_COLOR16 | ILC_MASK, 6, 0);
или так
himl = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 6, 0);
ImageList_AddIcon(himl, hIcon);

без всяких байтомодификаций ???
ava
Alca | 23.01.2013, 11:20 #
Dem_max, в этом проекте есть свои нюансы, каждый битмап храниться в массиве.
ava
Alca | 04.02.2013, 22:49 #
Цитата



  typedef union
  {
    unsigned short Color; // 16bit
    struct
    {
    unsigned char Hi;
    unsigned char Low;
    } Parts;
    struct
    {
    unsigned short r: 4;
    unsigned short g: 4;
    unsigned short b: 4;
    unsigned short a: 4;
    } ARGB;
  } UColor16bit;


  bpl = 4 * bmp.bmWidth; /* bytes per line */
  src = (UCHAR*)lpbitmap;
  UColor16bit cColor;
  for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
    tmp = src;
    for (x = 0; x < bmp.bmWidth; x++) {
    cColor.Color = 0;


    cColor.ARGB.r = tmp[0] & 0xF;
    cColor.ARGB.g = tmp[1] & 0xF;
    cColor.ARGB.b = tmp[2] & 0xF;
    cColor.ARGB.a = tmp[3] & 0xF;


    dst[0] = cColor.Parts.Hi;
    dst[1] = cColor.Parts.Low;


    dst += 2; // 16bit
    tmp += 4;
    }
  }




Не работает!!
ava
Alca | 04.02.2013, 22:52 #
Цитата



  bpl = 4 * bmp.bmWidth; /* bytes per line */
  src = lpbitmap;
  for (y = 0; y < bmp.bmHeight; y++, src += bpl) {
    tmp = src;
    for (x = 0; x < bmp.bmWidth; x++) {
    if (wBitsPixel > 16) {
     alpha = tmp[3];


     dst[0] = tmp[0] * alpha / 255;
     dst[1] = tmp[1] * alpha / 255;
     dst[2] = tmp[2] * alpha / 255;
     dst[3] = alpha;
    } else {
     dst[0] = tmp[0];
     dst[1] = tmp[1];
     dst[2] = tmp[2];
     dst[3] = tmp[3];
    }


    dst += 4;
    tmp += 4;
    }
  }



ava
Alca | 04.02.2013, 22:53 #
Сама функция. Все эти скрины сделаны при 16 битном цвете.

Битмап так загружаю

HBITMAP load_bitmap(UINT id)
{
    HBITMAP hbitmap = NULL;

    hbitmap = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(id),
            IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION |
            LR_LOADTRANSPARENT | LR_SHARED);
    if (!hbitmap) {
        logf_err("LoadImage failed with %ld\n", GetLastError());
        return NULL;
    }

    return pre_multiple_rgb(hbitmap, 32);
}
ava
artsb | 05.02.2013, 08:32 #
Alca, можете дать ваш тестовый проект, чтобы мне не катать сначала?
ava
Alca | 05.02.2013, 12:33 #
Цитата


тестовый проект


Попробую его для начала сделать
ava
artsb | 05.02.2013, 15:51 #
Alca, а так не пробовали:
Цитата


Create a bitmap with the desired color depth as the destination bitmap and copy the original image to it.


Взято отсюда: How to convert a bmp of 32bppargb to a bmp of 16bpp.
ava
Alca | 05.02.2013, 17:18 #
Цитата


Попробую его для начала сделать


fail

Добавлено позднее:
Цитата


Create a bitmap with the desired color depth as the destination bitmap and copy the original image to it.


А где там код?
ava
mega | 05.02.2013, 17:24 #
Цитата (Alca @  5.2.2013,  19:18 findReferencedText)
А где там код?

А зачем он тебе? Просто загрузи 16-битный растр и скопируй его через BitBlt в 32-битный
ava
artsb | 05.02.2013, 17:29 #
Цитата (GremlinProg @  5.2.2013,  17:24 findReferencedText)
А зачем он тебе? Просто загрузи 16-битный растр и скопируй его через BitBlt в 32-битный 

ИМХО, наоборот smile
ava
Alca | 05.02.2013, 18:32 #
http://www.dreamincode.net/forums/topic/28...ent-in-c-win32/


HBITMAP MakeBitMapTransparent(HBITMAP hbmSrc)
{
    HDC hdcSrc, hdcDst;
    HBITMAP hbmOld, hbmNew;
    BITMAP bm;
    COLORREF clrTP, clrBK;

    if ((hdcSrc = CreateCompatibleDC(NULL)) != NULL) {
        if ((hdcDst = CreateCompatibleDC(NULL)) != NULL) {
            int nRow, nCol;
            GetObject(hbmSrc, sizeof(bm), &bm);
            hbmOld = (HBITMAP)SelectObject(hdcSrc, hbmSrc);
            hbmNew = CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, NULL);
            SelectObject(hdcDst, hbmNew);

                                
            BitBlt(hdcDst,0,0,bm.bmWidth, bm.bmHeight,hdcSrc,0,0,SRCCOPY);

            clrTP = GetPixel(hdcDst, 0, 0);// Get color of first pixel at 0,0
            clrBK = GetSysColor(COLOR_MENU);// Get the current background color of the menu

            for (nRow = 0; nRow < bm.bmHeight; nRow++)// work our way through all the pixels changing their color
                for (nCol = 0; nCol < bm.bmWidth; nCol++)// when we hit our set transparency color.
                    if (GetPixel(hdcDst, nCol, nRow) == clrTP)
                        SetPixel(hdcDst, nCol, nRow, clrBK);

            DeleteDC(hdcDst);
        }
        DeleteDC(hdcSrc);

    }
    return hbmNew;// return our transformed bitmap.
}



так??
ava
artsb | 05.02.2013, 18:54 #
По идее, так:

HBITMAP MakeBitMapTransparent(HBITMAP hbmSrc)
{
    HDC hdcSrc, hdcDst;
    HBITMAP hbmOldSrc, hbmOldDst, hbmNew;
    BITMAP bm;

    if ((hdcSrc = CreateCompatibleDC(NULL)) != NULL) {
        if ((hdcDst = CreateCompatibleDC(NULL)) != NULL) {
            GetObject(hbmSrc, sizeof(bm), &bm);
            hbmOldSrc = (HBITMAP)SelectObject(hdcSrc, hbmSrc);
            hbmNew = CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, 8, NULL);
            hbmOldDst = (HBITMAP)SelectObject(hdcDst, hbmNew);
                                
            BitBlt(hdcDst,0,0,bm.bmWidth, bm.bmHeight,hdcSrc,0,0,SRCCOPY);

            SelectObject(hdcDst, hbmOldDst);
            DeleteDC(hdcDst);
            SelectObject(hdcSrc, hbmOldSrc);
        }
        DeleteDC(hdcSrc);
    }
    return hbmNew;// return our transformed bitmap.
}


Добавлено позднее:
А! Понял смысл вашего перебора. По сути, да, у вас правильно. Только не забывайте восстанавливать старые битмапы. И, наверное, не стоит брать левый верхний пиксел, как цвет прозрачности. Проверяйте именно альфа-канал: если он равен 0, то заменяйте этот пиксел на свой цвет прозрачности, иначе - ничего не делайте. Но как мне кажется, лучше эту операцию проделать с исходным изображением ещё до BitBlt().
ava
artsb | 05.02.2013, 19:26 #
Только вот так:

hbmNew = CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, 8, NULL);
ava
Alca | 05.02.2013, 23:56 #
да какая-то лажа с этой прозрачностью в 16 битном цвете  smile 
ava
artsb | 06.02.2013, 08:05 #
Попробую протестить. А что конкретно не так?
ava
Alca | 06.02.2013, 12:36 #
Цитата


Попробую протестить. А что конкретно не так?


http://forum.vingrad.ru/index.php?showtopi...t&p=2546325
ava
Alca | 06.02.2013, 13:32 #
Желающим могу дать доступ через Team Viewer, в личку
ava
artsb | 06.02.2013, 15:01 #
А если так:

HBITMAP MakeBitMapTransparent(HBITMAP hbmSrc)
{
    HDC hdcSrc, hdcDst, dcScreen;
    HBITMAP hbmOldSrc, hbmOldDst, hbmNew = NULL;
    BITMAP bm;
    COLORREF clrTP, clrBK;

    dcScreen = GetDC(GetDesktopWindow());

    if ((hdcSrc = CreateCompatibleDC(dcScreen)) != NULL)
    {
        if ((hdcDst = CreateCompatibleDC(dcScreen)) != NULL)
        {
            int nRow, nCol;
            GetObject(hbmSrc, sizeof(bm), &bm);

            hbmOldSrc = (HBITMAP)SelectObject(hdcSrc, hbmSrc);
//            hbmNew = CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, NULL);
            hbmNew = CreateCompatibleBitmap(dcScreen, bm.bmWidth, bm.bmHeight);

            if(hbmNew != NULL)
            {
                hbmOldDst = (HBITMAP)SelectObject(hdcDst, hbmNew);

                BitBlt(hdcDst,0,0,bm.bmWidth, bm.bmHeight,hdcSrc,0,0,SRCCOPY);

                clrTP = GetPixel(hdcDst, 0, 0);// Get color of first pixel at 0,0
                clrBK = GetSysColor(COLOR_MENU);// Get the current background color of the menu
                for (nRow = 0; nRow < bm.bmHeight; nRow++)// work our way through all the pixels changing their color
                {
                    for (nCol = 0; nCol < bm.bmWidth; nCol++)// when we hit our set transparency color.
                    {
                        if (GetPixel(hdcDst, nCol, nRow) == clrTP)
                        {
                            SetPixel(hdcDst, nCol, nRow, clrBK);
                        }
                    }
                }

                SelectObject(hdcDst, hbmOldDst);
                DeleteDC(hdcDst);
            }

            SelectObject(hdcSrc, hbmOldSrc);
        }
        DeleteDC(hdcSrc);
    }
    return hbmNew;// return our transformed bitmap.
}
ava
Alca | 06.02.2013, 15:11 #
Цитата


А если так:


ёптель, аллилуя, тока фон светло серый, видно, что отличается
ava
artsb | 06.02.2013, 15:18 #
Цитата (Alca @  6.2.2013,  15:11 findReferencedText)
тока фон светло серый

Ну это COLOR_MENU. Меняйте на цвет фона контрола. Останется только один косяк - когда итем выделен.
ava
Alca | 06.02.2013, 15:29 #
Цитата


Останется только один косяк - когда итем выделен.


Кстати, не замечал
ava
artsb | 06.02.2013, 15:33 #
Цитата (Alca @  6.2.2013,  15:29 findReferencedText)
Кстати, не замечал 

Я имею ввиду, что фон картики будет отличаться от фона выделенной записи.
ava
Alca | 06.02.2013, 15:41 #
Цитата


Ну это COLOR_MENU. Меняйте на цвет фона контрола. 


А не меню???
На другой другой теме раб стола (упрощенной) все ок!
ava
artsb | 06.02.2013, 15:55 #
Цитата (Alca @  6.2.2013,  15:41 findReferencedText)
А не меню???

На другой другой теме раб стола (упрощенной) все ок! 

Я так понял, что у вас используется ListView. Вот его цвет и нужно брать.  По умолчанию это COLOR_WINDOW, вроде бы.
ava
Alca | 06.02.2013, 16:05 #
Цитата


Я так понял, что у вас используется ListView. Вот его цвет и нужно брать.  По умолчанию это COLOR_WINDOW, вроде бы.


На дефолтой схеме все ок! Но когда начинаешь перебирать другие схемы, то тут бываеют лажи (не на всех),
короче я заслал этот патч, жду, что скажут
ava
Alca | 08.02.2013, 11:56 #
Получил фидбек. Написали, что это грязный хак.  smile 
ava
Alca | 08.02.2013, 12:20 #
Цитата


Альфаканал и вырезать

все пикселы такого же цвета как левый верхний это разные вещи

ava
artsb | 08.02.2013, 20:53 #
Ясно. Нужно подумать как сделать.
ava
Alca | 12.02.2013, 15:27 #
ну его <пип>...

M
GremlinProg
Alca
 smile 
Зарегистрируйтесь или войдите, чтобы написать.
Фирма дня
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Участники
  mega   Alca   Dem_max ava  artsb ava  feodorv
advanced
Отправить