You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
9.2 KiB
336 lines
9.2 KiB
/*
|
|
* kis_pattern.cc - part of Krayon
|
|
*
|
|
* Copyright (c) 2000 Matthias Elter <elter@kde.org>
|
|
* 2001 John Califf
|
|
* 2004 Boudewijn Rempt <boud@valdyas.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
#include "kis_pattern.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <tqpoint.h>
|
|
#include <tqsize.h>
|
|
#include <tqimage.h>
|
|
#include <tqvaluevector.h>
|
|
#include <tqmap.h>
|
|
#include <tqfile.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
|
|
#include "kis_color.h"
|
|
#include "kis_layer.h"
|
|
#include "kis_paint_device.h"
|
|
|
|
namespace {
|
|
struct GimpPatternHeader {
|
|
TQ_UINT32 header_size; /* header_size = sizeof (PatternHeader) + brush name */
|
|
TQ_UINT32 version; /* pattern file version # */
|
|
TQ_UINT32 width; /* width of pattern */
|
|
TQ_UINT32 height; /* height of pattern */
|
|
TQ_UINT32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/
|
|
TQ_UINT32 magic_number; /* GIMP brush magic number */
|
|
};
|
|
|
|
// Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT'
|
|
TQ_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0));
|
|
}
|
|
|
|
KisPattern::KisPattern(const TQString& file) : super(file), m_hasFile(true)
|
|
{
|
|
}
|
|
|
|
KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h)
|
|
: super(""), m_hasFile(false)
|
|
{
|
|
// Forcefully convert to RGBA8
|
|
// XXX profile and exposure?
|
|
setImage(image->convertToTQImage(0, x, y, w, h));
|
|
setName(image->name());
|
|
}
|
|
|
|
KisPattern::~KisPattern()
|
|
{
|
|
}
|
|
|
|
bool KisPattern::load()
|
|
{
|
|
if (!m_hasFile)
|
|
return true;
|
|
|
|
TQFile file(filename());
|
|
file.open(IO_ReadOnly);
|
|
TQByteArray data = file.readAll();
|
|
if (!data.isEmpty()) {
|
|
TQ_INT32 startPos = m_data.size();
|
|
|
|
m_data.resize(m_data.size() + data.count());
|
|
memcpy(&m_data[startPos], data.data(), data.count());
|
|
}
|
|
file.close();
|
|
return init();
|
|
}
|
|
|
|
bool KisPattern::save()
|
|
{
|
|
TQFile file(filename());
|
|
file.open(IO_WriteOnly | IO_Truncate);
|
|
|
|
TQTextStream stream(&file);
|
|
// Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name
|
|
// depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA
|
|
// magic = "GPAT", as a single uint32, the docs are wrong here!
|
|
// name is UTF-8 (\0-terminated! The docs say nothing about this!)
|
|
// _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!)
|
|
// We only save RGBA at the moment
|
|
// Version is 1 for now...
|
|
|
|
GimpPatternHeader ph;
|
|
TQCString utf8Name = name().utf8();
|
|
char const* name = utf8Name.data();
|
|
int nameLength = tqstrlen(name);
|
|
|
|
ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0
|
|
ph.version = htonl(1);
|
|
ph.width = htonl(width());
|
|
ph.height = htonl(height());
|
|
ph.bytes = htonl(4);
|
|
ph.magic_number = htonl(GimpPatternMagic);
|
|
|
|
TQByteArray bytes;
|
|
bytes.setRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
|
|
int wrote = file.writeBlock(bytes);
|
|
bytes.resetRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
|
|
|
|
if (wrote == -1)
|
|
return false;
|
|
|
|
wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly!
|
|
if (wrote == -1)
|
|
return false;
|
|
|
|
int k = 0;
|
|
bytes.resize(width() * height() * 4);
|
|
for (TQ_INT32 y = 0; y < height(); y++) {
|
|
for (TQ_INT32 x = 0; x < width(); x++) {
|
|
// RGBA only
|
|
TQRgb pixel = m_img.pixel(x,y);
|
|
bytes[k++] = static_cast<char>(tqRed(pixel));
|
|
bytes[k++] = static_cast<char>(tqGreen(pixel));
|
|
bytes[k++] = static_cast<char>(tqBlue(pixel));
|
|
bytes[k++] = static_cast<char>(tqAlpha(pixel));
|
|
}
|
|
}
|
|
|
|
wrote = file.writeBlock(bytes);
|
|
if (wrote == -1)
|
|
return false;
|
|
|
|
file.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
TQImage KisPattern::img()
|
|
{
|
|
return m_img;
|
|
}
|
|
|
|
bool KisPattern::init()
|
|
{
|
|
// load Gimp patterns
|
|
GimpPatternHeader bh;
|
|
TQ_INT32 k;
|
|
TQValueVector<char> name;
|
|
|
|
if (sizeof(GimpPatternHeader) > m_data.size()) {
|
|
return false;
|
|
}
|
|
|
|
memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader));
|
|
bh.header_size = ntohl(bh.header_size);
|
|
bh.version = ntohl(bh.version);
|
|
bh.width = ntohl(bh.width);
|
|
bh.height = ntohl(bh.height);
|
|
bh.bytes = ntohl(bh.bytes);
|
|
bh.magic_number = ntohl(bh.magic_number);
|
|
|
|
if (bh.header_size > m_data.size() || bh.header_size == 0) {
|
|
return false;
|
|
}
|
|
|
|
name.resize(bh.header_size - sizeof(GimpPatternHeader));
|
|
memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size());
|
|
|
|
if (name[name.size() - 1]) {
|
|
return false;
|
|
}
|
|
|
|
setName(i18n(&name[0]));
|
|
|
|
if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
|
|
return false;
|
|
}
|
|
|
|
k = bh.header_size;
|
|
|
|
if (bh.bytes == 1) {
|
|
// Grayscale
|
|
TQ_INT32 val;
|
|
|
|
for (TQ_UINT32 y = 0; y < bh.height; y++) {
|
|
for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
|
|
if (static_cast<TQ_UINT32>(k) > m_data.size()) {
|
|
kdDebug(DBG_AREA_FILE) << "failed in gray\n";
|
|
return false;
|
|
}
|
|
|
|
val = m_data[k];
|
|
m_img.setPixel(x, y, tqRgb(val, val, val));
|
|
m_img.setAlphaBuffer(false);
|
|
}
|
|
}
|
|
} else if (bh.bytes == 2) {
|
|
// Grayscale + A
|
|
TQ_INT32 val;
|
|
TQ_INT32 alpha;
|
|
for (TQ_UINT32 y = 0; y < bh.height; y++) {
|
|
for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
|
|
if (static_cast<TQ_UINT32>(k + 2) > m_data.size()) {
|
|
kdDebug(DBG_AREA_FILE) << "failed in grayA\n";
|
|
return false;
|
|
}
|
|
|
|
val = m_data[k];
|
|
alpha = m_data[k++];
|
|
m_img.setPixel(x, y, tqRgba(val, val, val, alpha));
|
|
m_img.setAlphaBuffer(true);
|
|
}
|
|
}
|
|
} else if (bh.bytes == 3) {
|
|
// RGB without alpha
|
|
for (TQ_UINT32 y = 0; y < bh.height; y++) {
|
|
for (TQ_UINT32 x = 0; x < bh.width; x++) {
|
|
if (static_cast<TQ_UINT32>(k + 3) > m_data.size()) {
|
|
kdDebug(DBG_AREA_FILE) << "failed in RGB\n";
|
|
return false;
|
|
}
|
|
|
|
m_img.setPixel(x, y, tqRgb(m_data[k],
|
|
m_data[k + 1],
|
|
m_data[k + 2]));
|
|
k += 3;
|
|
m_img.setAlphaBuffer(false);
|
|
}
|
|
}
|
|
} else if (bh.bytes == 4) {
|
|
// Has alpha
|
|
for (TQ_UINT32 y = 0; y < bh.height; y++) {
|
|
for (TQ_UINT32 x = 0; x < bh.width; x++) {
|
|
if (static_cast<TQ_UINT32>(k + 4) > m_data.size()) {
|
|
kdDebug(DBG_AREA_FILE) << "failed in RGBA\n";
|
|
return false;
|
|
}
|
|
|
|
m_img.setPixel(x, y, tqRgba(m_data[k],
|
|
m_data[k + 1],
|
|
m_data[k + 2],
|
|
m_data[k + 3]));
|
|
k += 4;
|
|
m_img.setAlphaBuffer(true);
|
|
}
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (m_img.isNull()) {
|
|
return false;
|
|
}
|
|
|
|
setWidth(m_img.width());
|
|
setHeight(m_img.height());
|
|
|
|
setValid(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) {
|
|
// Check if there's already a pattern prepared for this colorspace
|
|
TQMap<TQString, KisPaintDeviceSP>::const_iterator it = m_colorspaces.find(colorSpace->id().id());
|
|
if (it != m_colorspaces.end())
|
|
return (*it);
|
|
|
|
// If not, create one
|
|
KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern");
|
|
|
|
Q_CHECK_PTR(layer);
|
|
|
|
layer->convertFromTQImage(m_img,"");
|
|
|
|
m_colorspaces[colorSpace->id().id()] = layer;
|
|
return layer;
|
|
}
|
|
|
|
TQ_INT32 KisPattern::width() const
|
|
{
|
|
return m_width;
|
|
}
|
|
|
|
void KisPattern::setWidth(TQ_INT32 w)
|
|
{
|
|
m_width = w;
|
|
}
|
|
|
|
TQ_INT32 KisPattern::height() const
|
|
{
|
|
return m_height;
|
|
}
|
|
|
|
void KisPattern::setHeight(TQ_INT32 h)
|
|
{
|
|
m_height = h;
|
|
}
|
|
|
|
void KisPattern::setImage(const TQImage& img)
|
|
{
|
|
m_hasFile = false;
|
|
m_img = img;
|
|
m_img.detach();
|
|
|
|
setWidth(img.width());
|
|
setHeight(img.height());
|
|
|
|
setValid(true);
|
|
}
|
|
|
|
KisPattern* KisPattern::clone() const
|
|
{
|
|
KisPattern* pattern = new KisPattern("");
|
|
pattern->setImage(m_img);
|
|
pattern->setName(name());
|
|
return pattern;
|
|
}
|
|
|
|
#include "kis_pattern.moc"
|