Ejemplo n.º 1
0
void writeUncompressedData(QDataStream& stream, const ImageContainer& images, int pixelFormat) {
	// Mipmap offset
	if (images.hasMipmaps()) {
		writeZeroes(stream, MIPMAP_OFFSET_16BPP);
	}

	// Texture data, from smallest to largest mipmap
	for (int i=0; i<images.imageCount(); i++) {
		const QImage& img = images.getByIndex(i);

		// The 1x1 mipmap level is a bit special for YUV textures. Since there's only
		// one pixel, it can't be saved as YUV422, so save it as RGB565 instead.
		if (img.width() == 1 && img.height() == 1 && pixelFormat == PIXELFORMAT_YUV422) {
			convertAndWriteTexel(stream, img.pixel(0, 0), PIXELFORMAT_RGB565, true);
			continue;
		}

		const Twiddler twiddler(img.width(), img.height());
		const int pixels = img.width() * img.height();

		// Write all texels for this mipmap level in twiddled order
		for (int j=0; j<pixels; j++) {
			const int index = twiddler.index(j);
			const int x = index % img.width();
			const int y = index / img.width();
			convertAndWriteTexel(stream, img.pixel(x, y), pixelFormat, true);
		}
	}
}
Ejemplo n.º 2
0
// Divides the image into 2x2 pixel blocks and stores them as 16-dimensional
// vectors, (A, R, G, B) * 4.
static void vectorizeARGB(const ImageContainer& images, QVector<Vec<16>>& vectors) {
	for (int i=0; i<images.imageCount(); i++) {
		const QImage& img = images.getByIndex(i);

		// Ignore images smaller than this
		if (img.width() < MIN_MIPMAP_VQ || img.height() < MIN_MIPMAP_VQ)
			continue;

		for (int y=0; y<img.height(); y+=2) {
			for (int x=0; x<img.width(); x+=2) {
				Vec<16> vec;
				uint hash = 0;
				int offset = 0;
				for (int yy=y; yy<(y+2); yy++) {
					for (int xx=x; xx<(x+2); xx++) {
						QRgb pixel = img.pixel(xx, yy);
						argb2vec(pixel, vec, offset);
						hash = combineHash(pixel, hash);
						offset += 4;
					}
				}
				vec.setHash(hash);
				vectors.push_back(vec);
			}
		}
	}
}
Ejemplo n.º 3
0
static void devectorizeARGB(const ImageContainer& srcImages, const QVector<Vec<16>>& vectors, const VectorQuantizer<16>& vq, int format, QVector<QImage>& indexedImages, QVector<quint64>& codebook) {
	int vindex = 0;

	for (int i=0; i<srcImages.imageCount(); i++) {
		const QSize size = srcImages.getByIndex(i).size();
		if (size.width() == 1 || size.height() == 1)
			continue;
		QImage img(size.width()/2, size.height()/2, QImage::Format_Indexed8);
		img.setColorCount(256);
		for (int y=0; y<img.height(); y++) {
			for (int x=0; x<img.width(); x++) {
				const Vec<16>& vec = vectors[vindex];
				int codeIndex = vq.findClosest(vec);
				img.setPixel(x, y, codeIndex);
				vindex++;
			}
		}
		indexedImages.push_back(img);
	}

	for (int i=0; i<vq.codeCount(); i++) {
		const Vec<16>& vec = vq.codeVector(i);
		QColor tl = QColor::fromRgbF(vec[1], vec[2], vec[3], vec[0]);
		QColor tr = QColor::fromRgbF(vec[5], vec[6], vec[7], vec[4]);
		QColor bl = QColor::fromRgbF(vec[9], vec[10], vec[11], vec[8]);
		QColor br = QColor::fromRgbF(vec[13], vec[14], vec[15], vec[12]);
		quint64 quad = packQuad(tl.rgba(), tr.rgba(), bl.rgba(), br.rgba(), format);
		codebook.push_back(quad);
	}
}
Ejemplo n.º 4
0
void writeCompressedData(QDataStream& stream, const ImageContainer& images, int pixelFormat) {
	QVector<QImage> indexedImages;
	QVector<quint64> codebook;

	const int numQuads = encodeLossless(images, pixelFormat, indexedImages, codebook, 256);

	qDebug() << "Source images contain" << numQuads << "unique quads";

	if (numQuads > 256) {
		if ((pixelFormat != PIXELFORMAT_ARGB1555) && (pixelFormat != PIXELFORMAT_ARGB4444)) {
			QVector<Vec<12>> vectors;
			VectorQuantizer<12> vq;
			vectorizeRGB(images, vectors);
			vq.compress(vectors, 256);
			devectorizeRGB(images, vectors, vq, pixelFormat, indexedImages, codebook);
		} else {
			QVector<Vec<16>> vectors;
			VectorQuantizer<16> vq;
			vectorizeARGB(images, vectors);
			vq.compress(vectors, 256);
			devectorizeARGB(images, vectors, vq, pixelFormat, indexedImages, codebook);
		}
	}

	// Build the codebook
	quint16 codes[256 * 4];
	memset(codes, 0, 2048);
	for (int i=0; i<codebook.size(); i++) {
		const quint64& quad = codebook[i];
		codes[i * 4 + 0] = (quint16)((quad >> 48) & 0xFFFF);
		codes[i * 4 + 1] = (quint16)((quad >> 16) & 0xFFFF);
		codes[i * 4 + 2] = (quint16)((quad >> 32) & 0xFFFF);
		codes[i * 4 + 3] = (quint16)((quad >>  0) & 0xFFFF);
	}

	// Write the codebook
	for (int i=0; i<1024; i++)
		stream << codes[i];

	// Write the 1x1 mipmap level
	if (images.imageCount() > 1)
		writeZeroes(stream, 1);

	// Write all mipmap levels
	for (int i=0; i<indexedImages.size(); i++) {
		const QImage& img = indexedImages[i];
		const Twiddler twiddler(img.width(), img.height());
		const int pixels = img.width() * img.height();

		for (int j=0; j<pixels; j++) {
			const int index = twiddler.index(j);
			const int x = index % img.width();
			const int y = index / img.width();
			stream << (quint8)img.pixelIndex(x, y);
		}
	}
}
Ejemplo n.º 5
0
// This function counts how many unique 2x2 16BPP pixel blocks there are in the image.
// If there are <= maxCodes, it puts the unique blocks in 'codebook' and 'indexedImages'
// will contain images that index the 'codebook' vector, resulting in quick "lossless"
// compression, if possible.
// It will keep counting blocks even if the block count exceeds maxCodes for the sole
// purpose of reporting it back to the user.
// Returns number of unique 2x2 16BPP pixel blocks in all images.
static int encodeLossless(const ImageContainer& images, int pixelFormat, QVector<QImage>& indexedImages, QVector<quint64>& codebook, int maxCodes) {
	QHash<quint64, int> uniqueQuads; // Quad <=> index

	for (int i=0; i<images.imageCount(); i++) {
		const QImage& img = images.getByIndex(i);

		// Ignore images smaller than this
		if (img.width() < MIN_MIPMAP_VQ || img.height() < MIN_MIPMAP_VQ)
			continue;

		QImage indexedImage(img.width() / 2, img.height() / 2, QImage::Format_Indexed8);
		indexedImage.setColorCount(256);

		for (int y=0; y<img.height(); y+=2) {
			for (int x=0; x<img.width(); x+=2) {
				QRgb tl = img.pixel(x + 0, y + 0);
				QRgb tr = img.pixel(x + 1, y + 0);
				QRgb bl = img.pixel(x + 0, y + 1);
				QRgb br = img.pixel(x + 1, y + 1);
				quint64 quad = packQuad(tl, tr, bl, br, pixelFormat);

				if (!uniqueQuads.contains(quad))
					uniqueQuads.insert(quad, uniqueQuads.size());

				if (uniqueQuads.size() <= maxCodes)
					indexedImage.setPixel(x / 2, y / 2, uniqueQuads.value(quad));
			}
		}

		// Only add the image if we haven't hit the code limit
		if (uniqueQuads.size() <= maxCodes) {
			indexedImages.push_back(indexedImage);
		}
	}

	if (uniqueQuads.size() <= maxCodes) {
		// This texture can be losslessly compressed.
		// Copy the unique quads over to the codebook.
		// indexedImages is already done.
		codebook.resize(uniqueQuads.size());
		for (auto it = uniqueQuads.cbegin(); it != uniqueQuads.cend(); ++it)
			codebook[it.value()] = it.key();
	} else {
		// This texture needs lossy compression
		indexedImages.clear();
	}

	return uniqueQuads.size();
}