chalk

kis_pattern.cpp
1/*
2 * kis_pattern.cpp - part of Krayon
3 *
4 * Copyright (c) 2000 Matthias Elter <elter@kde.org>
5 * 2001 John Califf
6 * 2004 Boudewijn Rempt <boud@valdyas.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22#include "kis_pattern.h"
23
24#include <sys/types.h>
25#include <netinet/in.h>
26
27#include <limits.h>
28#include <stdlib.h>
29
30#include <tqpoint.h>
31#include <tqsize.h>
32#include <tqimage.h>
33#include <tqvaluevector.h>
34#include <tqmap.h>
35#include <tqfile.h>
36
37#include <kdebug.h>
38#include <tdelocale.h>
39
40#include "kis_color.h"
41#include "kis_layer.h"
42#include "kis_paint_device.h"
43
44namespace {
45 struct GimpPatternHeader {
46 TQ_UINT32 header_size; /* header_size = sizeof (PatternHeader) + brush name */
47 TQ_UINT32 version; /* pattern file version # */
48 TQ_UINT32 width; /* width of pattern */
49 TQ_UINT32 height; /* height of pattern */
50 TQ_UINT32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/
51 TQ_UINT32 magic_number; /* GIMP brush magic number */
52 };
53
54 // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT'
55 TQ_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0));
56}
57
58KisPattern::KisPattern(const TQString& file) : super(file), m_hasFile(true)
59{
60}
61
62KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h)
63 : super(""), m_hasFile(false)
64{
65 // Forcefully convert to RGBA8
66 // XXX profile and exposure?
67 setImage(image->convertToTQImage(0, x, y, w, h));
68 setName(image->name());
69}
70
71KisPattern::~KisPattern()
72{
73}
74
75bool KisPattern::load()
76{
77 if (!m_hasFile)
78 return true;
79
80 TQFile file(filename());
81 file.open(IO_ReadOnly);
82 TQByteArray data = file.readAll();
83 if (!data.isEmpty()) {
84 TQ_INT32 startPos = m_data.size();
85
86 m_data.resize(m_data.size() + data.count());
87 memcpy(&m_data[startPos], data.data(), data.count());
88 }
89 file.close();
90 return init();
91}
92
93bool KisPattern::save()
94{
95 TQFile file(filename());
96 file.open(IO_WriteOnly | IO_Truncate);
97
98 TQTextStream stream(&file);
99 // Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name
100 // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA
101 // magic = "GPAT", as a single uint32, the docs are wrong here!
102 // name is UTF-8 (\0-terminated! The docs say nothing about this!)
103 // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!)
104 // We only save RGBA at the moment
105 // Version is 1 for now...
106
107 GimpPatternHeader ph;
108 TQCString utf8Name = name().utf8();
109 char const* name = utf8Name.data();
110 int nameLength = tqstrlen(name);
111
112 ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0
113 ph.version = htonl(1);
114 ph.width = htonl(width());
115 ph.height = htonl(height());
116 ph.bytes = htonl(4);
117 ph.magic_number = htonl(GimpPatternMagic);
118
119 TQByteArray bytes;
120 bytes.setRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
121 int wrote = file.writeBlock(bytes);
122 bytes.resetRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
123
124 if (wrote == -1)
125 return false;
126
127 wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly!
128 if (wrote == -1)
129 return false;
130
131 int k = 0;
132 bytes.resize(width() * height() * 4);
133 for (TQ_INT32 y = 0; y < height(); y++) {
134 for (TQ_INT32 x = 0; x < width(); x++) {
135 // RGBA only
136 TQRgb pixel = m_img.pixel(x,y);
137 bytes[k++] = static_cast<char>(tqRed(pixel));
138 bytes[k++] = static_cast<char>(tqGreen(pixel));
139 bytes[k++] = static_cast<char>(tqBlue(pixel));
140 bytes[k++] = static_cast<char>(tqAlpha(pixel));
141 }
142 }
143
144 wrote = file.writeBlock(bytes);
145 if (wrote == -1)
146 return false;
147
148 file.close();
149
150 return true;
151}
152
153TQImage KisPattern::img()
154{
155 return m_img;
156}
157
158bool KisPattern::init()
159{
160 // load Gimp patterns
161 GimpPatternHeader bh;
162 TQ_INT32 k;
163 TQValueVector<char> name;
164
165 if (sizeof(GimpPatternHeader) > m_data.size()) {
166 return false;
167 }
168
169 memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader));
170 bh.header_size = ntohl(bh.header_size);
171 bh.version = ntohl(bh.version);
172 bh.width = ntohl(bh.width);
173 bh.height = ntohl(bh.height);
174 bh.bytes = ntohl(bh.bytes);
175 bh.magic_number = ntohl(bh.magic_number);
176
177 if (bh.header_size > m_data.size() || bh.header_size == 0) {
178 return false;
179 }
180
181 name.resize(bh.header_size - sizeof(GimpPatternHeader));
182 memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size());
183
184 if (name[name.size() - 1]) {
185 return false;
186 }
187
188 setName(i18n(&name[0]));
189
190 if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
191 return false;
192 }
193
194 k = bh.header_size;
195
196 if (bh.bytes == 1) {
197 // Grayscale
198 TQ_INT32 val;
199
200 for (TQ_UINT32 y = 0; y < bh.height; y++) {
201 for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
202 if (static_cast<TQ_UINT32>(k) > m_data.size()) {
203 kdDebug(DBG_AREA_FILE) << "failed in gray\n";
204 return false;
205 }
206
207 val = m_data[k];
208 m_img.setPixel(x, y, tqRgb(val, val, val));
209 m_img.setAlphaBuffer(false);
210 }
211 }
212 } else if (bh.bytes == 2) {
213 // Grayscale + A
214 TQ_INT32 val;
215 TQ_INT32 alpha;
216 for (TQ_UINT32 y = 0; y < bh.height; y++) {
217 for (TQ_UINT32 x = 0; x < bh.width; x++, k++) {
218 if (static_cast<TQ_UINT32>(k + 2) > m_data.size()) {
219 kdDebug(DBG_AREA_FILE) << "failed in grayA\n";
220 return false;
221 }
222
223 val = m_data[k];
224 alpha = m_data[k++];
225 m_img.setPixel(x, y, tqRgba(val, val, val, alpha));
226 m_img.setAlphaBuffer(true);
227 }
228 }
229 } else if (bh.bytes == 3) {
230 // RGB without alpha
231 for (TQ_UINT32 y = 0; y < bh.height; y++) {
232 for (TQ_UINT32 x = 0; x < bh.width; x++) {
233 if (static_cast<TQ_UINT32>(k + 3) > m_data.size()) {
234 kdDebug(DBG_AREA_FILE) << "failed in RGB\n";
235 return false;
236 }
237
238 m_img.setPixel(x, y, tqRgb(m_data[k],
239 m_data[k + 1],
240 m_data[k + 2]));
241 k += 3;
242 m_img.setAlphaBuffer(false);
243 }
244 }
245 } else if (bh.bytes == 4) {
246 // Has alpha
247 for (TQ_UINT32 y = 0; y < bh.height; y++) {
248 for (TQ_UINT32 x = 0; x < bh.width; x++) {
249 if (static_cast<TQ_UINT32>(k + 4) > m_data.size()) {
250 kdDebug(DBG_AREA_FILE) << "failed in RGBA\n";
251 return false;
252 }
253
254 m_img.setPixel(x, y, tqRgba(m_data[k],
255 m_data[k + 1],
256 m_data[k + 2],
257 m_data[k + 3]));
258 k += 4;
259 m_img.setAlphaBuffer(true);
260 }
261 }
262 } else {
263 return false;
264 }
265
266 if (m_img.isNull()) {
267 return false;
268 }
269
270 setWidth(m_img.width());
271 setHeight(m_img.height());
272
273 setValid(true);
274
275 return true;
276}
277
278KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) {
279 // Check if there's already a pattern prepared for this colorspace
280 TQMap<TQString, KisPaintDeviceSP>::const_iterator it = m_colorspaces.find(colorSpace->id().id());
281 if (it != m_colorspaces.end())
282 return (*it);
283
284 // If not, create one
285 KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern");
286
287 TQ_CHECK_PTR(layer);
288
289 layer->convertFromTQImage(m_img,"");
290
291 m_colorspaces[colorSpace->id().id()] = layer;
292 return layer;
293}
294
295TQ_INT32 KisPattern::width() const
296{
297 return m_width;
298}
299
300void KisPattern::setWidth(TQ_INT32 w)
301{
302 m_width = w;
303}
304
305TQ_INT32 KisPattern::height() const
306{
307 return m_height;
308}
309
310void KisPattern::setHeight(TQ_INT32 h)
311{
312 m_height = h;
313}
314
315void KisPattern::setImage(const TQImage& img)
316{
317 m_hasFile = false;
318 m_img = img;
319 m_img.detach();
320
321 setWidth(img.width());
322 setHeight(img.height());
323
324 setValid(true);
325}
326
327KisPattern* KisPattern::clone() const
328{
329 KisPattern* pattern = new KisPattern("");
330 pattern->setImage(m_img);
331 pattern->setName(name());
332 return pattern;
333}
334
335#include "kis_pattern.moc"
A colorspace is the definition of a certain color model in Chalk.
virtual KisID id() const =0
Chalk definition for use in .kra files and internally: unchanging name + i18n'able description.
A paint device contains the actual pixel data and offers methods to read and write pixels.
virtual TQImage convertToTQImage(KisProfile *dstProfile, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, float exposure=0.0f)
Create an RGBA TQImage from a rectangle in the paint device.