Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Игра Ксоникс (http://forum.oszone.net/showthread.php?t=127231)

Doom77 28-12-2008 11:16 992087

Игра Ксоникс
 
Пишу игру Ксоникс на C. Подскажите как вырезать картинку.

Есть масив
0000000000
0000000000
0000000000
0000000000

Пользователь произвольно ставит 1.

0000000000
0000000000
0000001111
0000001000


0000100000
0000100000
1111100000
0000000000

Как эти элементы поменять на 2?
1111
1000


00001
00001
11111

pva 28-12-2008 23:21 992569

Если не прибегать к библиотекам сторонних производителей, например gdi32.dll от микрософт, не пользоваться алгоритмами стандартной библиотеки c++, то код будет выглядеть примерно так:
Код:

typedef int surf_bit_t;

struct rect_t
{
  int left, top, right, bottom;
}

struct surface_desc
{
  surf_bit_t *begin; // начало данных
  unsigned width, height, stride; // длина, ширина блока, длина строчки
};

void __blt(surface_desc* from, surface_desc* to)
{
    // копирование без проверки исходых данных.
    // чтобы случайно не попортить память при записи, руководствуемся размерами конечной области
    for(surf_bit_t *row_first1=to->begin,
      *row_last1=to->begin+from->stride*to->width,
      *row_first2=from->from->begin;
        row_first1<row_last1; row_first1+=to->stride, row_first2+=from->stride)
    {
// закомментирован пример кода для копирования,
// можешь поиграться с ним (переделать в закрашивание)
//        for(unsigned col=0; col<to->width; ++col)
//        {
//            row_first1[col] = row_first2[col];
//        }
          memcpy(row_first2, row_first1, to->width*sizeof(surf_bit_t));
    }
}

inline int __constraint(int x, int min, int max)
{
  return x < min ? min : (x < max ? x : max);
}

void blt(surface_desc* from, int x, int y, int width, int height, surface_desc* to, int to_x, int to_y)
{
  // более удобный для пользователя интерфейс
  // с проверкой исходных данных
  // вычисляем перекрывающийся прямоугольник для копирования.

  surface_desc from1, to1;

  {
        // пересечение прямоугольника {0, 0, width, height} с
        // исходным изображением, в координатах копируемого окошка
        rect_t rect_src = {
                __constraint(-x, 0, width),
                __constraint(-y, 0, height),
                __constraint(from->width - x, 0, width),
                __constraint(from->height - y, 0, height)},
        // пересечение  {0, 0, width, height} с
        // конечным изображением и сразу с пересечённым исходным rect_src
                  rect_dest = {
                __constraint(__constraint(-x_to, 0, width), rect_src.left, rect_src.right),
                __constraint(__constraint(-y_to, 0, height), rect_src.top, rect_src.bottom),
                __constraint(__constraint(to->width - x_to, 0, width), rect_src.left, rect_src.right),
                __constraint(__constraint(to->height - y_to, 0, height), rect_src.top, rect_src.bottom)};
       
        // составляем полётное задание для __blt

        from1.begin  = from->begin + (rect_dest.top + y)*from->stride + rect_dest.left + x;
        from1.stride = from->stride;
        from1.width  = rect_desc.right - rect_dest.left;
        from1.height = rect_desc.bottom - rect_dest.top;

        to1.begin  = to->begin + (rect_dest.top + to_y)*from->stride + rect_dest.left + to_x;
        to1.stride = to->stride;
        to1.width  = rect_desc.right - rect_dest.left;
        to1.height = rect_desc.bottom - rect_dest.top;
  } 

  __blt(&from1, &to1);
}

...

// как пользоваться

surf_bit_t screen1_bits[320*200];
surface_desc screen1 = {&screen1_bits, 320, 200, 320};

surf_bit_t screen2_bits[50*100];
surface_desc screen2 = {&screen2_bits, 50, 100, 50};

...

blt(&screen1, 100, 100, 60, 60, &screen2, 10, 10);

если внутри __blt заменить memcpy на memset, то получится "закрашивание". Рекомендую почитать в сторону функции BitBlt от винды. Если surf_bit_t - 8 битный, то можно сделать 8-битные картинки в памяти и смело пользоваться функциями GDI

Doom77 30-12-2008 18:31 993884

Не могу разобраться в коде не могли бы вы упростить код или объяснить доходчивей

pva 31-12-2008 12:03 994424

Цитата:

Цитата Doom77
не могли бы вы упростить код »

только если подняться на высокий уровень (С++).
Добавил комментариев
Код:

// это чтобы можно было легко подменить тип-носитель
// для "цвета" ячейки
typedef int surf_bit_t;

struct rect_t
{
    // прямоугольник - понадобится для пересечений
  int left, top, right, bottom;
}

// стркуктура, которая указывает, в каком виде хранится картинка.
// данные хранятся в виде линейного массива определённой ширины строчки
//                    на картинке нарисован массив, у которого stride = 6,
//    x x x x x x    width = 4, height = 3. Можно сказать, что мы работаем
//    x x 1 1 1 1                  с большим массивом 6x5, а можно что с
//    x x 1 0 0 0    1 1 1 1 ? ?  маленьким 4x3, строчки которого
//    x x 1 0 0 0    1 0 0 0 ? ?  расположены через 6 элементов.
//    x x x x x x    1 0 0 0 ? ?  begin в последнем случае указывает на
//                                  начало "маленького" массива.

struct surface_desc
{
  surf_bit_t *begin; // начало данных
  unsigned width, height, stride; // длина, ширина блока, длина строчки
};

void __blt(surface_desc* from, surface_desc* to)
{
    // функция НЕ ПОЗВОЛЯЕТ копировать в себя, то есть когда from и to могут пересекаться.
    // копирование без проверки исходых данных.
    // чтобы случайно не попортить память при записи, руководствуемся размерами конечной области

    // эта функция используется как внутренняя, исходные данные должны быть уже проверены.
    // в качестве аргументов получает поверхности экрана, описанные выше (surface_desc)

    // row_first1 пробегает строчки, куда копировать, от начала до конца, с шагом stride
    // row_last1 - это и есть конец (посчитали и запомнили)
    // row_first1 пробегает строчки, откуда копировать, с шагом stride

    for(surf_bit_t *row_first1=to->begin,
      *row_last1=to->begin+from->stride*to->width,
      *row_first2=from->from->begin;
        row_first1<row_last1; row_first1+=to->stride, row_first2+=from->stride)
    {
        // закомментирован пример кода для копирования,
        // можешь поиграться с ним (переделать в закрашивание)

        for(unsigned col=0; col<to->width; ++col)
        {
            // копирование
            row_first1[col] = row_first2[col];

            // а это могло бы быть закрашивание
            // row_first1[col] = 2;
        }

        // простейший случай - просто копирование памяти. Сэкономил код функцией
        //  memcpy(row_first2, row_first1, to->width*sizeof(surf_bit_t));
    }
}

inline int __constraint(int x, int min, int max)
{
    // ограничивает значение x - прибмвает к одному из порогов.
    // нужно для пересечения прямоугольников
  return x < min ? min : (x < max ? x : max);
}

//                                          функция, которая вычисляет аргументы для __blt
//    x x x x x x                          по заданным исходному прямоугольнику и конечной
//    x x 1 1 1 1        y 1 1 1 1 y y    точке, куда содержимое этого прямоугольника
//    x x 1 0 0 0  ->    y 1 0 0 0 y y    скопировать.
//    x x 1 0 0 0        y 1 0 0 0 y y    Надо учесть случаи, когда часть прямоугольника
//    x x x x x x                          будет выходить за пределы исходного или
//                                          конечного экранов. Для этого сделаем следующее:
// (1) пересчитаем координаты прямоугольников экранов в координатах исходного и конечного
// прямоугольника, (2) пересечём их с исходным и конечным прямоугольником. Затем (3)
// пересечём что получилось между собой. Получится прямоугольник (4), который и надо
// копировать. Пересчитаем его в координатах экранов (5), запишем в виде поверхностей (6) и
// отдадим функции __blt
//
//  (0,0)              (0,0)                  (0,0)
//    +---+ (w,0)        +---+ (w,0)            +---+ (w,0)
//    |  |              |  |                  |  |
//    |  x|x x x        |y y|y y y    ->      |  N|    область, которая_
//    |  x|x x x        |y y|y y y            |  N|    будет скопирована
//    +--x+x x x        +y-y+y y y            +--N+
// (0,h) x x x x      (0,h)                  (0,h)
//


void blt(surface_desc* from, /*поверхность-откуда*/
        int x, int y,  /*координаты, откуда копировать*/
        int width, int height,  /*размеры окошка*/
        surface_desc* to, /*поверхность-куда*/
        int to_x, int to_y) /*координаты куда*/
{
  // более удобный для пользователя интерфейс
  // с проверкой исходных данных
  // вычисляем перекрывающийся прямоугольник для копирования.

  surface_desc from1, to1;

  {
    // пересечение прямоугольника {0, 0, width, height} с
    // исходным изображением, в координатах копируемого окошка
    rect_t rect_src = {
        /*2*/__constraint(/*1*/-x, 0, width),
        /*2*/__constraint(/*1*/-y, 0, height),
        /*2*/__constraint(/*1*/from->width - x, 0, width),
        /*2*/__constraint(/*1*/from->height - y, 0, height)},
    // пересечение  {0, 0, width, height} с
    // конечным изображением и сразу с пересечённым исходным rect_src
        rect_dest = /*4*/{
        /*3*/__constraint(/*2*/__constraint(/*1*/-x_to, 0, width), rect_src.left, rect_src.right),
        /*3*/__constraint(/*2*/__constraint(/*1*/-y_to, 0, height), rect_src.top, rect_src.bottom),
        /*3*/__constraint(/*2*/__constraint(/*1*/to->width - x_to, 0, width), rect_src.left, rect_src.right),
        /*3*/__constraint(/*2*/__constraint(/*1*/to->height - y_to, 0, height), rect_src.top, rect_src.bottom)};

    // составляем полётное задание для __blt

    from1.begin  = /*6*/from->begin + (/*5*/rect_dest.top + y)*from->stride + /*5*/rect_dest.left + x;
    from1.stride = /*6*/from->stride; /*сохраняется для подобласти*/
    from1.width  = /*6*/rect_desc.right - rect_dest.left;
    from1.height = /*6*/rect_desc.bottom - rect_dest.top;

    to1.begin  = /*6*/to->begin + (/*5*/rect_dest.top + to_y)*from->stride + /*5*/rect_dest.left + to_x;
    to1.stride = /*6*/to->stride;    /*сохраняется для подобласти*/
    to1.width  = /*6*/rect_desc.right - rect_dest.left;
    to1.height = /*6*/rect_desc.bottom - rect_dest.top;
  }

  __blt(&from1, &to1);
}


// как пользоваться_
// где-то в программе выделена память под экран, сделано описание для этой памяти:

surf_bit_t screen1_bits[/*width*/320 * /*height*/200];
surface_desc screen1 = {/*begin*/ &screen1_bits, /*width*/320, /*height*/200, /*stride=width*/320};

surf_bit_t screen2_bits[/*width*/50 * /*height*/100];
surface_desc screen2 = {/*begin*/ &screen2_bits, /*width*/50, /*height*/100, /*stride=width*/50};

// в том месте, где надо скопировать делаем так:
// копируем прямоугольник 60x60 из точки (100,100) в (10,10) на другом экране
blt(&screen1, 100, 100, 60, 60, &screen2, 10, 10);



Время: 08:34.

Время: 08:34.
© OSzone.net 2001-