mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
225 lines
6.5 KiB
C++
225 lines
6.5 KiB
C++
/**
|
|
* PIC_RW - Qt PIC Support
|
|
* Copyright (C) 2007 Ruben Lopez <r.lopez@bren.es>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* This code is based on the GIMP-PIC plugin by Halfdan Ingvarsson,
|
|
* and relicensed from GPL to LGPL to accomodate the KDE licensing policy
|
|
* with his permission.
|
|
* These is the original copyright:
|
|
* Copyright (C) 1998 Halfdan Ingvarsson
|
|
*/
|
|
|
|
#include "pic_rw.h"
|
|
#include <netinet/in.h>
|
|
#include <iostream>
|
|
#include <qimage.h>
|
|
|
|
/**
|
|
* Writes the PIC header info.
|
|
* @param dev IO Device
|
|
* @param msg Header message
|
|
* @param width Image width
|
|
* @param height Image height
|
|
* @param alpha Image has alpha?
|
|
* @return True on success
|
|
*/
|
|
static bool writeHeader(QIODevice *dev, std::string msg, unsigned width, unsigned height, bool alpha) {
|
|
PICHeader h;
|
|
PICChannel c;
|
|
unsigned count = 0;
|
|
|
|
memset(&h, 0, sizeof (PICHeader));
|
|
h.magic = htonl(PIC_MAGIC_NUMBER);
|
|
h.version = 3.71f;
|
|
strcpy(h.comment, msg.c_str());
|
|
strncpy(h.id, "PICT", 4);
|
|
h.width = htons(width);
|
|
h.height = htons(height);
|
|
h.ratio = 1.0f;
|
|
h.fields = htons(BOTH);
|
|
count = dev->write((const char*) & h, sizeof (PICHeader));
|
|
if (count != sizeof (PICHeader)) {
|
|
return false;
|
|
}
|
|
|
|
memset(&c, 0, sizeof (PICChannel));
|
|
c.size = 8;
|
|
c.type = RLE;
|
|
c.channel = RED | GREEN | BLUE;
|
|
if (alpha) {
|
|
c.chained = 1;
|
|
}
|
|
count = dev->write((const char*) & c, sizeof (PICChannel));
|
|
if (count != sizeof (PICChannel)) {
|
|
return false;
|
|
}
|
|
|
|
if (alpha) {
|
|
c.channel = ALPHA;
|
|
c.chained = 0;
|
|
count = dev->write((const char*) & c, sizeof (PICChannel));
|
|
if (count != sizeof (PICChannel)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline unsigned convertABGRtoRGBA(unsigned pixel) {
|
|
unsigned r = pixel & 0xFF;
|
|
unsigned g = (pixel >> 8) & 0xFF;
|
|
unsigned b = (pixel >> 16) & 0xFF;
|
|
unsigned a = (pixel >> 24) & 0xFF;
|
|
return a | (b << 8) | (g << 16) | (r << 24);
|
|
}
|
|
|
|
/**
|
|
* Encodes a portion of the image in RLE coding
|
|
* @param image The image that we want to encode
|
|
* @param output The output buffer
|
|
* @param channels The number of channels to write
|
|
* @param offset Offset in bytes to copy
|
|
* @param max The maximum number of pixels to write
|
|
* @param oConsumed The number of pixels consumed from image
|
|
* @param oProduced The number of bytes produced in out
|
|
* @return True on success
|
|
*/
|
|
static bool encodeRLE(const unsigned *image, unsigned char *output, bool rgb, unsigned max, unsigned &oConsumed, unsigned &oProduced) {
|
|
const unsigned *in = image;
|
|
unsigned char *out = output;
|
|
unsigned count = 0;
|
|
unsigned channels = 3;
|
|
unsigned offset = 1;
|
|
unsigned mask = 0x00FFFFFF;
|
|
if (!rgb) {
|
|
channels = 1;
|
|
offset = 0;
|
|
mask = 0xFF000000;
|
|
}
|
|
for (; (*in & mask) == (*image & mask) && count < 65536 && count < max; in++, count++) {
|
|
}
|
|
if (count > 127) {
|
|
/* Sequence of > 127 identical pixels */
|
|
*out++ = 128;
|
|
*out++ = count >> 8;
|
|
*out++ = count & 0xFF;
|
|
unsigned pixel = convertABGRtoRGBA(*image);
|
|
memcpy(out, ((char*) & pixel) + offset, channels);
|
|
out += channels;
|
|
oConsumed = count;
|
|
oProduced = out - output;
|
|
}
|
|
else if (count > 1) {
|
|
/* Sequece of < 128 identical pixels */
|
|
*out++ = (count + 127);
|
|
unsigned pixel = convertABGRtoRGBA(*image);
|
|
memcpy(out, ((char*) & pixel) + offset, channels);
|
|
out += channels;
|
|
oConsumed = count;
|
|
oProduced = out - output;
|
|
}
|
|
else {
|
|
in = image + 1;
|
|
unsigned previous = *image;
|
|
count = 0;
|
|
while ((*in & mask) != (previous & mask) && count < 128 && count < max) {
|
|
previous = *in;
|
|
in++;
|
|
count++;
|
|
}
|
|
// This only happens when it is the end of the row, and it is ok
|
|
if (count == 0) {
|
|
count = 1;
|
|
}
|
|
*out++ = (count - 1);
|
|
in = image;
|
|
for (unsigned c = 0; c < count; ++c) {
|
|
unsigned pixel = convertABGRtoRGBA(*in);
|
|
memcpy(out, ((char*) & pixel) + offset, channels);
|
|
out += channels;
|
|
in++;
|
|
}
|
|
oConsumed = count;
|
|
oProduced = out - output;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Writes a row to the file
|
|
* @return True on success
|
|
*/
|
|
static bool writeRow(QIODevice *dev, unsigned *row, unsigned width, bool alpha) {
|
|
unsigned char *buf = new unsigned char[width * 4];
|
|
unsigned posIn = 0;
|
|
unsigned posOut = 0;
|
|
|
|
memset(buf, 0, width * 4);
|
|
|
|
unsigned consumed = 0;
|
|
unsigned produced = 0;
|
|
|
|
/* Write the RGB part of the scanline */
|
|
while (posIn < width) {
|
|
if (!encodeRLE(row + posIn, buf + posOut, true, width - posIn, consumed, produced)) {
|
|
delete[] buf;
|
|
return false;
|
|
}
|
|
posIn += consumed;
|
|
posOut += produced;
|
|
}
|
|
|
|
/* Write the alpha channel */
|
|
if (alpha) {
|
|
posIn = 0;
|
|
while (posIn < width) {
|
|
if (!encodeRLE(row + posIn, buf + posOut, false, width - posIn, consumed, produced)) {
|
|
delete[] buf;
|
|
return false;
|
|
}
|
|
posIn += consumed;
|
|
posOut += produced;
|
|
}
|
|
}
|
|
|
|
dev->write((const char*) buf, posOut);
|
|
delete[] buf;
|
|
return true;
|
|
}
|
|
|
|
#define FAIL() { \
|
|
std::cout << "ERROR Writing PIC!" << std::endl; \
|
|
return; \
|
|
}
|
|
|
|
/// Pic write handler for Qt / KDE
|
|
|
|
void pic_write(QIODevice *dev, const QImage *img) {
|
|
bool alpha = img->hasAlphaChannel();
|
|
if (!writeHeader(dev, "Created with KDE", img->width(), img->height(), alpha)) {
|
|
FAIL();
|
|
}
|
|
|
|
for (int r = 0; r < img->height(); r++) {
|
|
unsigned *row = (unsigned*) img->scanLine(r);
|
|
if (!writeRow(dev, row, img->width(), alpha)) {
|
|
FAIL();
|
|
}
|
|
}
|
|
}
|