Saves between 2x and 1000x of space when passing them to the gui.
It's also a huge performance win because the json parser needs to
parse between 2x and 1000x less data.
print '#include <iostream>'
print '#include <algorithm>'
print
+ print '#include "image.hpp"'
print '#include "json.hpp"'
print '#include "glimports.hpp"'
print '#include "glproc.hpp"'
glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
json.beginMember("__data__");
- json.writeBase64(pixels, depth * width * height * 4 * sizeof *pixels);
+ char *pngBuffer;
+ int pngBufferSize;
+ Image::writePixelsToBuffer(pixels, width, height, 4, false, &pngBuffer, &pngBufferSize);
+ json.writeBase64(pngBuffer, pngBufferSize);
+ free(pngBuffer);
json.endMember(); // __data__
delete [] pixels;
glReadBuffer(readbuffer);
json.beginMember("__data__");
- json.writeBase64(pixels, width * height * channels * sizeof *pixels);
+ char *pngBuffer;
+ int pngBufferSize;
+ Image::writePixelsToBuffer(pixels, width, height, channels, false, &pngBuffer, &pngBufferSize);
+ //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
+ // <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
+ json.writeBase64(pngBuffer, pngBufferSize);
+ free(pngBuffer);
json.endMember(); // __data__
delete [] pixels;
m_numChannels = numChannels;
}
-static inline int
-rgba8_to_argb(quint8 r, quint8 g, quint8 b, quint8 a)
-{
- return (a << 24 | r << 16 | g << 8 | b);
-}
-
-static inline int
-rgbaf2argb(float r, float g, float b, float a)
-{
- quint8 rb = r * 255;
- quint8 gb = g * 255;
- quint8 bb = b * 255;
- quint8 ab = a * 255;
-
- return (ab << 24 | rb << 16 | gb << 8 | bb);
-}
-
void ApiSurface::contentsFromBase64(const QByteArray &base64)
{
QByteArray dataArray = QByteArray::fromBase64(base64);
- const quint8 *data = (const quint8*)dataArray.data();
- int width = m_size.width();
- int height = m_size.height();
-
- if (width <= 0 || height <= 0)
- return;
-
- int *pixelData = (int*)malloc(sizeof(int) * width * height);
-
- //XXX not sure if this will work when
- // QSysInfo::ByteOrder == QSysInfo::BigEndian
-
- if (m_numChannels == 4) {
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int pixel = rgba8_to_argb(data[(y * width + x) * 4 + 0],
- data[(y * width + x) * 4 + 1],
- data[(y * width + x) * 4 + 2],
- data[(y * width + x) * 4 + 3]);
- pixelData[y * width + x] = pixel;
- }
- }
- } else if (m_numChannels == 1) {
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int pixel = rgba8_to_argb(data[y * width + x],
- data[y * width + x],
- data[y * width + x],
- 255);
- pixelData[y * width + x] = pixel;
- }
- }
- } else {
- Q_ASSERT(0);
- }
-
- m_image = QImage((uchar*)pixelData,
- width, height,
- QImage::Format_ARGB32).mirrored();
+ m_image.loadFromData(dataArray, "png");
+ m_image = m_image.mirrored();
m_thumb = m_image.scaled(64, 64, Qt::KeepAspectRatio);
- //m_image.save("testoutput.png");
-
- free(pixelData);
}
QImage ApiSurface::image() const
#include <math.h>
#include <stdint.h>
+#include <stdlib.h>
#include <fstream>
return precision;
}
+struct png_tmp_buffer
+{
+ char *buffer;
+ size_t size;
+};
+
+static void
+pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ struct png_tmp_buffer *buf = (struct png_tmp_buffer*) png_ptr->io_ptr;
+ size_t nsize = buf->size + length;
+
+ /* allocate or grow buffer */
+ if (buf->buffer)
+ buf->buffer = (char*)realloc(buf->buffer, nsize);
+ else
+ buf->buffer = (char*)malloc(nsize);
+
+ if (!buf->buffer)
+ png_error(png_ptr, "Buffer allocation error");
+
+ memcpy(buf->buffer + buf->size, data, length);
+ buf->size += length;
+}
+
+bool writePixelsToBuffer(unsigned char *pixels,
+ unsigned width, unsigned height, unsigned numChannels,
+ bool flipped,
+ char **buffer,
+ int *size)
+{
+ struct png_tmp_buffer png_mem;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ int type;
+
+ png_mem.buffer = NULL;
+ png_mem.size = 0;
+
+ switch (numChannels) {
+ case 4:
+ type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case 3:
+ type = PNG_COLOR_TYPE_RGB;
+ break;
+ case 2:
+ type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case 1:
+ type = PNG_COLOR_TYPE_GRAY;
+ break;
+ default:
+ goto no_png;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ goto no_png;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ goto no_png;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ goto no_png;
+ }
+
+ png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8,
+ type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
+
+ png_write_info(png_ptr, info_ptr);
+
+ if (!flipped) {
+ for (unsigned y = 0; y < height; ++y) {
+ png_bytep row = (png_bytep)(pixels + y*width*numChannels);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ } else {
+ unsigned y = height;
+ while (y--) {
+ png_bytep row = (png_bytep)(pixels + y*width*numChannels);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ *buffer = png_mem.buffer;
+ *size = png_mem.size;
+
+ return true;
+
+no_png:
+ *buffer = NULL;
+ *size = 0;
+
+ if (png_mem.buffer)
+ free(png_mem.buffer);
+ return false;
+}
} /* namespace Image */
double compare(Image &ref);
};
+bool writePixelsToBuffer(unsigned char *pixels,
+ unsigned w, unsigned h, unsigned numChannels,
+ bool flipped,
+ char **buffer,
+ int *size);
Image *
readPNG(const char *filename);