krita

kis_pattern.cc

00001 /*
00002  *  kis_pattern.cc - part of Krayon
00003  *
00004  *  Copyright (c) 2000 Matthias Elter <elter@kde.org>
00005  *                2001 John Califf
00006  *                2004 Boudewijn Rempt <boud@valdyas.org>
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  */
00022 #include "kis_pattern.h"
00023 
00024 #include <sys/types.h>
00025 #include <netinet/in.h>
00026 
00027 #include <limits.h>
00028 #include <stdlib.h>
00029 
00030 #include <qpoint.h>
00031 #include <qsize.h>
00032 #include <qimage.h>
00033 #include <qvaluevector.h>
00034 #include <qmap.h>
00035 #include <qfile.h>
00036 
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039 
00040 #include "kis_color.h"
00041 #include "kis_layer.h"
00042 #include "kis_paint_device.h"
00043 
00044 namespace {
00045     struct GimpPatternHeader {
00046         Q_UINT32 header_size;  /*  header_size = sizeof (PatternHeader) + brush name  */
00047         Q_UINT32 version;      /*  pattern file version #  */
00048         Q_UINT32 width;        /*  width of pattern */
00049         Q_UINT32 height;       /*  height of pattern  */
00050         Q_UINT32 bytes;        /*  depth of pattern in bytes : 1, 2, 3 or 4*/
00051         Q_UINT32 magic_number; /*  GIMP brush magic number  */
00052     };
00053 
00054     // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT'
00055     Q_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0));
00056 }
00057 
00058 KisPattern::KisPattern(const QString& file) : super(file), m_hasFile(true)
00059 {
00060 }
00061 
00062 KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h)
00063     : super(""), m_hasFile(false)
00064 {
00065     // Forcefully convert to RGBA8
00066     // XXX profile and exposure?
00067     setImage(image->convertToQImage(0, x, y, w, h));
00068     setName(image->name());
00069 }
00070 
00071 KisPattern::~KisPattern()
00072 {
00073 }
00074 
00075 bool KisPattern::load()
00076 {
00077     if (!m_hasFile)
00078         return true;
00079 
00080     QFile file(filename());
00081     file.open(IO_ReadOnly);
00082     QByteArray data = file.readAll();
00083     if (!data.isEmpty()) {
00084         Q_INT32 startPos = m_data.size();
00085 
00086         m_data.resize(m_data.size() + data.count());
00087         memcpy(&m_data[startPos], data.data(), data.count());
00088     }
00089     file.close();
00090     return init();
00091 }
00092 
00093 bool KisPattern::save()
00094 {
00095     QFile file(filename());
00096     file.open(IO_WriteOnly | IO_Truncate);
00097 
00098     QTextStream stream(&file);
00099     // Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name
00100     // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA
00101     // magic = "GPAT", as a single uint32, the docs are wrong here!
00102     // name is UTF-8 (\0-terminated! The docs say nothing about this!)
00103     // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!)
00104     // We only save RGBA at the moment
00105     // Version is 1 for now...
00106 
00107     GimpPatternHeader ph;
00108     QCString utf8Name = name().utf8();
00109     char const* name = utf8Name.data();
00110     int nameLength = qstrlen(name);
00111 
00112     ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0
00113     ph.version = htonl(1);
00114     ph.width = htonl(width());
00115     ph.height = htonl(height());
00116     ph.bytes = htonl(4);
00117     ph.magic_number = htonl(GimpPatternMagic);
00118 
00119     QByteArray bytes;
00120     bytes.setRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
00121     int wrote = file.writeBlock(bytes);
00122     bytes.resetRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
00123 
00124     if (wrote == -1)
00125         return false;
00126 
00127     wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly!
00128     if (wrote == -1)
00129         return false;
00130 
00131     int k = 0;
00132     bytes.resize(width() * height() * 4);
00133     for (Q_INT32 y = 0; y < height(); y++) {
00134         for (Q_INT32 x = 0; x < width(); x++) {
00135             // RGBA only
00136             QRgb pixel = m_img.pixel(x,y);
00137             bytes[k++] = static_cast<char>(qRed(pixel));
00138             bytes[k++] = static_cast<char>(qGreen(pixel));
00139             bytes[k++] = static_cast<char>(qBlue(pixel));
00140             bytes[k++] = static_cast<char>(qAlpha(pixel));
00141         }
00142     }
00143 
00144     wrote = file.writeBlock(bytes);
00145     if (wrote == -1)
00146         return false;
00147 
00148     file.close();
00149 
00150     return true;
00151 }
00152 
00153 QImage KisPattern::img()
00154 {
00155     return m_img;
00156 }
00157 
00158 bool KisPattern::init()
00159 {
00160     // load Gimp patterns
00161     GimpPatternHeader bh;
00162     Q_INT32 k;
00163     QValueVector<char> name;
00164 
00165     if (sizeof(GimpPatternHeader) > m_data.size()) {
00166         return false;
00167     }
00168 
00169     memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader));
00170     bh.header_size = ntohl(bh.header_size);
00171     bh.version = ntohl(bh.version);
00172     bh.width = ntohl(bh.width);
00173     bh.height = ntohl(bh.height);
00174     bh.bytes = ntohl(bh.bytes);
00175     bh.magic_number = ntohl(bh.magic_number);
00176 
00177     if (bh.header_size > m_data.size() || bh.header_size == 0) {
00178         return false;
00179     }
00180 
00181     name.resize(bh.header_size - sizeof(GimpPatternHeader));
00182     memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size());
00183 
00184     if (name[name.size() - 1]) {
00185         return false;
00186     }
00187 
00188     setName(i18n(&name[0]));
00189 
00190     if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
00191         return false;
00192     }
00193 
00194     k = bh.header_size;
00195 
00196     if (bh.bytes == 1) {
00197         // Grayscale
00198         Q_INT32 val;
00199 
00200         for (Q_UINT32 y = 0; y < bh.height; y++) {
00201             for (Q_UINT32 x = 0; x < bh.width; x++, k++) {
00202                 if (static_cast<Q_UINT32>(k) > m_data.size()) {
00203                     kdDebug(DBG_AREA_FILE) << "failed in gray\n";
00204                     return false;
00205                 }
00206 
00207                 val = m_data[k];
00208                 m_img.setPixel(x, y, qRgb(val, val, val));
00209                 m_img.setAlphaBuffer(false);
00210             }
00211         }
00212     } else if (bh.bytes == 2) {
00213         // Grayscale + A
00214         Q_INT32 val;
00215         Q_INT32 alpha;
00216         for (Q_UINT32 y = 0; y < bh.height; y++) {
00217             for (Q_UINT32 x = 0; x < bh.width; x++, k++) {
00218                 if (static_cast<Q_UINT32>(k + 2) > m_data.size()) {
00219                     kdDebug(DBG_AREA_FILE) << "failed in grayA\n";
00220                     return false;
00221                 }
00222 
00223                 val = m_data[k];
00224                 alpha = m_data[k++];
00225                 m_img.setPixel(x, y, qRgba(val, val, val, alpha));
00226                 m_img.setAlphaBuffer(true);
00227             }
00228         }
00229     } else if (bh.bytes == 3) {
00230         // RGB without alpha
00231         for (Q_UINT32 y = 0; y < bh.height; y++) {
00232             for (Q_UINT32 x = 0; x < bh.width; x++) {
00233                 if (static_cast<Q_UINT32>(k + 3) > m_data.size()) {
00234                     kdDebug(DBG_AREA_FILE) << "failed in RGB\n";
00235                     return false;
00236                 }
00237 
00238                 m_img.setPixel(x, y, qRgb(m_data[k],
00239                               m_data[k + 1],
00240                               m_data[k + 2]));
00241                 k += 3;
00242                 m_img.setAlphaBuffer(false);
00243             }
00244         }
00245     } else if (bh.bytes == 4) {
00246         // Has alpha
00247         for (Q_UINT32 y = 0; y < bh.height; y++) {
00248             for (Q_UINT32 x = 0; x < bh.width; x++) {
00249                 if (static_cast<Q_UINT32>(k + 4) > m_data.size()) {
00250                     kdDebug(DBG_AREA_FILE) << "failed in RGBA\n";
00251                     return false;
00252                 }
00253 
00254                 m_img.setPixel(x, y, qRgba(m_data[k],
00255                                m_data[k + 1],
00256                                m_data[k + 2],
00257                                m_data[k + 3]));
00258                 k += 4;
00259                 m_img.setAlphaBuffer(true);
00260             }
00261         }
00262     } else {
00263         return false;
00264     }
00265 
00266     if (m_img.isNull()) {
00267         return false;
00268     }
00269 
00270     setWidth(m_img.width());
00271     setHeight(m_img.height());
00272 
00273     setValid(true);
00274 
00275     return true;
00276 }
00277 
00278 KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) {
00279     // Check if there's already a pattern prepared for this colorspace
00280     QMap<QString, KisPaintDeviceSP>::const_iterator it = m_colorspaces.find(colorSpace->id().id());
00281     if (it != m_colorspaces.end())
00282         return (*it);
00283 
00284     // If not, create one
00285     KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern");
00286 
00287     Q_CHECK_PTR(layer);
00288 
00289     layer->convertFromQImage(m_img,"");
00290 
00291     m_colorspaces[colorSpace->id().id()] = layer;
00292     return layer;
00293 }
00294 
00295 Q_INT32 KisPattern::width() const
00296 {
00297     return m_width;
00298 }
00299 
00300 void KisPattern::setWidth(Q_INT32 w)
00301 {
00302     m_width = w;
00303 }
00304 
00305 Q_INT32 KisPattern::height() const
00306 {
00307     return m_height;
00308 }
00309 
00310 void KisPattern::setHeight(Q_INT32 h)
00311 {
00312     m_height = h;
00313 }
00314 
00315 void KisPattern::setImage(const QImage& img)
00316 {
00317     m_hasFile = false;
00318     m_img = img;
00319     m_img.detach();
00320 
00321     setWidth(img.width());
00322     setHeight(img.height());
00323 
00324     setValid(true);
00325 }
00326 
00327 KisPattern* KisPattern::clone() const
00328 {
00329     KisPattern* pattern = new KisPattern("");
00330     pattern->setImage(m_img);
00331     pattern->setName(name());
00332     return pattern;
00333 }
00334 
00335 #include "kis_pattern.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys