3 #include <QPixmapCache>
5 #include "common/util.h"
6 #include "common/color.h"
10 #define TILE_PIXELS (TILE_SIZE * TILE_SIZE)
11 #define MAGIC 0x1423D5FF
13 static quint8
bpp(quint8 colours
)
33 static bool validateTable(const QVector
<quint8
> &table
)
37 for (int i
= 0; i
< table
.size(); i
++) {
38 if (table
.at(i
) == 128) {
39 if (i
+ 2 >= table
.size())
41 delta
= 65537 - (256 * table
.at(i
+2) + table
.at(i
+1)) + 2;
42 if (i
+ delta
>= table
.size())
45 } else if (table
.at(i
) > 128) {
46 delta
= 257 - table
.at(i
);
47 if (i
+ delta
>= table
.size())
55 static bool createTable(QDataStream
&stream
, QVector
<quint8
> &table
)
63 while (stream
.status() == QDataStream::Ok
&& colours
<= branches
) {
64 table
.resize(table
.size() + 1);
67 if (table
[idx
] == 128) {
68 table
.resize(table
.size() + 2);
69 stream
>> table
[++idx
];
70 stream
>> table
[++idx
];
72 } else if (table
[idx
] > 128)
80 return (stream
.status() == QDataStream::Ok
);
83 static bool huffman(QDataStream
&stream
, quint8 tileData
[TILE_PIXELS
])
85 QVector
<quint8
> table
;
86 if (!createTable(stream
, table
))
89 if (table
.size() == 1) {
90 memset(tileData
, table
[0], TILE_PIXELS
);
92 if (!validateTable(table
))
95 const quint8
*tp
= table
.constData();
102 for (int pixelnum
= 0; pixelnum
< TILE_PIXELS
; ) {
104 tileData
[pixelnum
++] = *tp
;
105 tp
= table
.constData();
124 tp
+= 65537 - (256 * tp
[2] + tp
[1]) + 2;
130 return (stream
.status() == QDataStream::Ok
);
133 static bool pixelPacking(QDataStream
&stream
, quint8 tileData
[TILE_PIXELS
],
136 quint8 shift
= bpp(colours
);
137 quint32 mask
= (1 << shift
) - 1;
138 int wordSize
= 32 / shift
;
139 quint8 paletteIndex
[256];
141 for (quint8 i
= 0; i
< colours
; i
++)
142 stream
>> paletteIndex
[i
];
144 for (int pixelnum
= 0; pixelnum
< TILE_PIXELS
; ) {
148 for (int runs
= 0; runs
< wordSize
; runs
++) {
151 tileData
[pixelnum
++] = paletteIndex
[colour
];
155 return (stream
.status() == QDataStream::Ok
);
158 static bool rle(QDataStream
&stream
, quint8 tileData
[TILE_PIXELS
],
161 quint8 bits
= bpp(colours
);
162 quint8 paletteMask
= (1 << bits
) - 1;
163 quint8 paletteIndex
[256];
166 for (quint8 i
= 0; i
< colours
; i
++)
167 stream
>> paletteIndex
[i
];
169 for (int pixelnum
= 0; pixelnum
< TILE_PIXELS
; ) {
172 quint8 colour
= val
& paletteMask
;
173 quint8 runs
= val
>> bits
;
176 tileData
[pixelnum
++] = paletteIndex
[colour
];
179 return (stream
.status() == QDataStream::Ok
);
182 static bool readString(QDataStream
&stream
, quint32 offset
, QString
&str
)
187 if (!stream
.device()->seek(offset
))
190 while (stream
.readRawData(&c
, 1) == 1) {
194 str
= QString::fromUtf8(ba
);
202 bool QCTMap::readName(QDataStream
&stream
)
206 stream
>> title
>> name
;
207 if (stream
.status() != QDataStream::Ok
)
211 if (!readString(stream
, name
, _name
))
214 if (!readString(stream
, title
, _name
))
217 _name
= Util::file2name(path());
222 bool QCTMap::readSize(QDataStream
&stream
)
224 stream
>> _cols
>> _rows
;
225 return (stream
.status() == QDataStream::Ok
);
228 bool QCTMap::readDatumShift(QDataStream
&stream
)
232 if (!stream
.device()->seek(0x54))
235 if (stream
.status() != QDataStream::Ok
)
239 if (!stream
.device()->seek(ext
+ 4))
242 if (stream
.status() != QDataStream::Ok
)
246 if (!stream
.device()->seek(shift
))
248 stream
>> _shiftN
>> _shiftE
;
250 return (stream
.status() == QDataStream::Ok
);
253 bool QCTMap::readHeader(QDataStream
&stream
)
255 quint32 magic
, version
;
256 stream
>> magic
>> version
;
258 if (stream
.status() != QDataStream::Ok
|| magic
!= MAGIC
) {
259 _errorString
= "Not a QCT map";
262 if (version
== 0x20000001) {
263 _errorString
= "QC3 files not supported";
266 if (!readSize(stream
)) {
267 _errorString
= "Error reading map dimensions";
270 if (!readName(stream
)) {
271 _errorString
= "Error reading map name";
274 if (!readDatumShift(stream
)) {
275 _errorString
= "Error reading datum shift";
282 bool QCTMap::readGeoRef(QDataStream
&stream
)
284 if (!stream
.device()->seek(0x60))
287 stream
>> _eas
>> _easY
>> _easX
>> _easYY
>> _easXY
>> _easXX
>> _easYYY
288 >> _easYYX
>> _easXXY
>> _easXXX
>> _nor
>> _norY
>> _norX
>> _norYY
289 >> _norXY
>> _norXX
>> _norYYY
>> _norYYX
>> _norXXY
>> _norXXX
;
290 stream
>> _lat
>> _latX
>> _latY
>> _latXX
>> _latXY
>> _latYY
>> _latXXX
291 >> _latXXY
>> _latXYY
>> _latYYY
>> _lon
>> _lonX
>> _lonY
>> _lonXX
292 >> _lonXY
>> _lonYY
>> _lonXXX
>> _lonXXY
>> _lonXYY
>> _lonYYY
;
294 return (stream
.status() == QDataStream::Ok
);
297 bool QCTMap::readPalette(QDataStream
&stream
)
299 if (!stream
.device()->seek(0x01A0))
302 _palette
.resize(256);
305 for (int i
= 0; i
< _palette
.size(); i
++) {
307 _palette
[i
] = Color::bgr2rgb(bgr
);
310 return (stream
.status() == QDataStream::Ok
);
313 bool QCTMap::readIndex(QDataStream
&stream
)
315 if (!stream
.device()->seek(0x45A0))
318 _index
.resize(_cols
* _rows
);
319 for (int i
= 0; i
< _cols
* _rows
; i
++)
322 return (stream
.status() == QDataStream::Ok
);
325 QCTMap::QCTMap(const QString
&fileName
, QObject
*parent
)
326 : Map(fileName
, parent
), _file(fileName
), _shiftE(0), _shiftN(0),
327 _mapRatio(1.0), _valid(false)
329 if (!_file
.open(QIODevice::ReadOnly
)) {
330 _errorString
= _file
.errorString();
334 QDataStream
stream(&_file
);
335 stream
.setByteOrder(QDataStream::LittleEndian
);
337 if (!readHeader(stream
))
339 if (!readGeoRef(stream
)) {
340 _errorString
= "Error reading georeference info";
343 if (!readPalette(stream
)) {
344 _errorString
= "Error reading colour palette";
347 if (!readIndex(stream
)) {
348 _errorString
= "Error reading tile index";
357 void QCTMap::load(const Projection
&in
, const Projection
&out
,
358 qreal deviceRatio
, bool hidpi
)
363 _mapRatio
= hidpi
? deviceRatio
: 1.0;
364 _file
.open(QIODevice::ReadOnly
);
367 void QCTMap::unload()
372 QRectF
QCTMap::bounds()
374 return QRectF(QPointF(0, 0), QSizeF(_cols
* TILE_SIZE
, _rows
* TILE_SIZE
)
378 QPointF
QCTMap::ll2xy(const Coordinates
&c
)
380 double lon
= c
.lon() - _shiftE
;
381 double lon2
= lon
* lon
;
382 double lon3
= lon2
* lon
;
383 double lat
= c
.lat() - _shiftN
;
384 double lat2
= lat
* lat
;
385 double lat3
= lat2
* lat
;
387 double x
= _easXXX
*lon3
+ _easXX
*lon2
+ _easX
*lon
+ _easYYY
*lat3
388 + _easYY
*lat2
+ _easY
*lat
+ _easXXY
*lon2
*lat
389 + _easYYX
*lat2
*lon
+ _easXY
*lon
*lat
+ _eas
;
390 double y
= _norXXX
*lon3
+ _norXX
*lon2
+ _norX
*lon
+ _norYYY
*lat3
391 + _norYY
*lat2
+ _norY
*lat
+ _norXXY
*lon2
*lat
392 + _norYYX
*lat2
*lon
+ _norXY
*lon
*lat
+ _nor
;
394 return QPointF(x
- _shiftE
, y
- _shiftN
) / _mapRatio
;
397 Coordinates
QCTMap::xy2ll(const QPointF
&p
)
399 qreal x
= p
.x() * _mapRatio
;
402 qreal y
= p
.y() * _mapRatio
;
406 double lon
= _lon
+ _lonX
*x
+ _lonY
*y
+ _lonXX
*x2
407 + _lonXY
*x
*y
+ _lonYY
*y2
+ _lonXXX
*x3
+ _lonXXY
*x2
*y
408 + _lonXYY
*x
*y2
+ _lonYYY
*y3
;
409 double lat
= _lat
+ _latX
*x
+ _latY
*y
+ _latXX
*x2
410 + _latXY
*x
*y
+ _latYY
*y2
+ _latXXX
*x3
+ _latXXY
*x2
* y
411 + _latXYY
*x
*y2
+ _latYYY
*y3
;
413 return Coordinates(lon
+ _shiftE
, lat
+ _shiftN
);
416 QPixmap
QCTMap::tile(int x
, int y
)
418 static quint8 rowSeq
[] = {
419 0, 32, 16, 48, 8, 40, 24, 56, 4, 36, 20, 52, 12, 44, 28, 60,
420 2, 34, 18, 50, 10, 42, 26, 58, 6, 38, 22, 54, 14, 46, 30, 62,
421 1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61,
422 3, 35, 19, 51, 11, 43, 27, 59, 7, 39, 23, 55, 15, 47, 31, 63
424 quint8 tileData
[TILE_PIXELS
], imgData
[TILE_PIXELS
];
429 if (!_file
.seek(_index
.at(y
* _cols
+ x
)))
432 QDataStream
stream(&_file
);
433 stream
.setByteOrder(QDataStream::LittleEndian
);
436 if (stream
.status() != QDataStream::Ok
)
439 if (packing
== 0 || packing
== 255)
440 ret
= huffman(stream
, tileData
);
441 else if (packing
> 127)
442 ret
= pixelPacking(stream
, tileData
, 256 - packing
);
444 ret
= rle(stream
, tileData
, packing
);
449 for (int i
= 0; i
< TILE_SIZE
; i
++)
450 memcpy(imgData
+ i
* TILE_SIZE
, tileData
+ rowSeq
[i
] * TILE_SIZE
,
453 QImage
img(imgData
, TILE_SIZE
, TILE_SIZE
, TILE_SIZE
,
454 QImage::Format_Indexed8
);
455 img
.setColorTable(_palette
);
457 return QPixmap::fromImage(img
);
460 void QCTMap::draw(QPainter
*painter
, const QRectF
&rect
, Flags flags
)
464 QSizeF
ts(TILE_SIZE
/ _mapRatio
, TILE_SIZE
/ _mapRatio
);
465 QPointF
tl(floor(rect
.left() / ts
.width()) * ts
.width(),
466 floor(rect
.top() / ts
.height()) * ts
.height());
468 QSizeF
s(rect
.right() - tl
.x(), rect
.bottom() - tl
.y());
469 for (int i
= 0; i
< ceil(s
.width() / ts
.width()); i
++) {
470 for (int j
= 0; j
< ceil(s
.height() / ts
.height()); j
++) {
471 int x
= round(tl
.x() * _mapRatio
+ i
* TILE_SIZE
) / TILE_SIZE
;
472 int y
= round(tl
.y() * _mapRatio
+ j
* TILE_SIZE
) / TILE_SIZE
;
475 QString key
= path() + "/" + QString::number(x
) + "_"
476 + QString::number(y
);
477 if (!QPixmapCache::find(key
, &pixmap
)) {
479 if (!pixmap
.isNull())
480 QPixmapCache::insert(key
, pixmap
);
484 qWarning("%s: error loading tile image", qPrintable(key
));
486 pixmap
.setDevicePixelRatio(_mapRatio
);
487 QPointF
tp(tl
.x() + i
* ts
.width(), tl
.y() + j
* ts
.height());
488 painter
->drawPixmap(tp
, pixmap
);
494 Map
*QCTMap::create(const QString
&path
, const Projection
&proj
, bool *isDir
)
501 return new QCTMap(path
);