Завершив чтение файла, проверяем состояние потока QDataStream. Так можно поступать, потому что, если QDataStream переходит в состояние ошибки, это состояние сохраняется в дальнейшем и последующие операции чтения могут выдать только нули. Например, если чтение первого массива бит завершается неудачей, попытка чтения второго массива в результате даст пустой массив QBitArray.
конструируем новый объект QImage с правильными размерами и устанавливаем на него указатель изображения. Затем проходим по каждому пикселю битовых массивов XOR и AND и преобразуем их в 32-битовый цветовой формат ARGB. С помощью массивов битов AND и XOR цвет каждого пикселя курсора всегда получается в соответствии со следующей таблицей:
С получением черного, белого и прозрачного пикселей нет проблем, однако нельзя получить инвертированный пиксель фона, используя цветовой формат ARGB, если не знаешь цвет исходного пикселя фона. В качестве замены используем полупрозрачный серый цвет (0x7F7F7F7F).
54 ++currentImageNo;
55 if (currentImageNo == numImages)
56 state = AfterLastImage;
57 return true;
58 }
Завершив чтение изображения, мы обновляем текущий номер изображения и обновляем состояние, если прочитано последнее изображение. В конце функции устройство будет указывать на начало следующего изображения или на конец файла.
01 bool CursorHandler::jumpToNextImage
02 {
03 QImage image;
04 return read(&image);
05 }
Функция jumpToNextImage используется для пропуска изображения. Для простоты мы всего лишь вызываем read и игнорируем полученный QImage. В более эффективной реализации использовалась бы информация, содержащаяся в заголовке файла .cur, для непосредственного смещения по файлу на соответствующее значение.
12 if (in.status != QDataStream::Ok || reserved != 0
13 || type != 2 || count == 0) {
14 enterErrorState;
15 return;
16 }
17 state = BeforeImage;
18 currentImageNo = 0;
19 numImages = int(count);
20 }
Закрытая
функция readHeaderIfNecessary вызывается из imageCount и read. Если заголовок файла уже был прочитан, состояние не будет иметь значение BeforeHeader (перед заголовком) и сразу же делается возврат управления. В противном случае открываем на устройстве поток данных, считываем некоторые общие данные (в частности, количество курсоров, содержащихся в файле) и устанавливаем состояние в значение BeforeImage (перед изображением). В конце указатель файла данного устройства устанавливается перед первым изображением.
01 void CursorHandler::enterErrorState const
02 {
03 currentImageNo = 0;
04 numImages = 0;
05 state = Error;
06 }
При возникновении ошибки считаем, что файл не содержит изображений требуемого формата, и устанавливаем состояние в значение Error. В дальнейшем такое состояние обработчика не может быть изменено.
01 QBitArray CursorHandler::readBitmap(int width, int height,
02 QDataStream &in) const
03 {
04 QBitArray bitmap(width * height);
05 quint8 byte;
06 quint32 word;
07 for (int i = 0; i < height; ++i) {
08 for (int j = 0; j < width; ++j) {
09 if ((j % 32) == 0) {
10 word = 0;
11 for (int k = 0; k < 4; ++k) {
12 in >> byte;
13 word = (word << 8) | byte;
14 }
15 }
16 bitmap.setBit(((height - i - 1) * width) + j,
17 word & 0x80000000);
18 word <<= 1;
19 }
20 }
21 return bitmap;
22 }
Функция readBitmap используется для чтения масок курсора AND и XOR. Эти маски обладают двумя необычными свойствами. Во-первых, строки в них располагаются, начиная с нижних, вместо обычного расположения строк сверху вниз. Во-вторых, оказывается, что используемый здесь порядок байтов отличается от порядка байтов любых других данных в файлах .cur. В связи с этим нам приходится инвертировать координату у в вызове setBit и считывать маски побайтно, сдвигая биты и используя маску для получения правильных значений.