KImgIO
xcf.cpp
Go to the documentation of this file.
00001 /* 00002 * qxcfi.cpp: A Qt 3 plug-in for reading GIMP XCF image files 00003 * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com> 00004 * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org> 00005 * 00006 * This plug-in is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 * 00020 */ 00021 00022 #include "xcf.h" 00023 00024 #include <stdlib.h> 00025 #include <QtGui/QImage> 00026 #include <QtGui/QPainter> 00027 #include <QtCore/QIODevice> 00028 #include <QtCore/QStack> 00029 #include <QtCore/QVector> 00030 00031 #include <kdebug.h> 00032 00033 00034 int XCFImageFormat::random_table[RANDOM_TABLE_SIZE]; 00035 bool XCFImageFormat::random_table_initialized; 00036 00037 QVector<QRgb> XCFImageFormat::grayTable; 00038 00039 00040 const XCFImageFormat::LayerModes XCFImageFormat::layer_modes[] = { 00041 {true}, // NORMAL_MODE 00042 {true}, // DISSOLVE_MODE 00043 {true}, // BEHIND_MODE 00044 {false}, // MULTIPLY_MODE 00045 {false}, // SCREEN_MODE 00046 {false}, // OVERLAY_MODE 00047 {false}, // DIFFERENCE_MODE 00048 {false}, // ADDITION_MODE 00049 {false}, // SUBTRACT_MODE 00050 {false}, // DARKEN_ONLY_MODE 00051 {false}, // LIGHTEN_ONLY_MODE 00052 {false}, // HUE_MODE 00053 {false}, // SATURATION_MODE 00054 {false}, // COLOR_MODE 00055 {false}, // VALUE_MODE 00056 {false}, // DIVIDE_MODE 00057 {false}, // DODGE_MODE 00058 {false}, // BURN_MODE 00059 {false}, // HARDLIGHT_MODE 00060 {false}, // SOFTLIGHT_MODE 00061 {false}, // GRAIN_EXTRACT_MODE 00062 {false}, // GRAIN_MERGE_MODE 00063 }; 00064 00065 00067 inline QRgb qRgba ( const QRgb& rgb, int a ) 00068 { 00069 return ((a & 0xff) << 24 | (rgb & RGB_MASK)); 00070 } 00071 00072 00076 XCFImageFormat::XCFImageFormat() 00077 { 00078 } 00079 00083 void XCFImageFormat::initializeRandomTable() 00084 { 00085 // From GIMP "paint_funcs.c" v1.2 00086 srand(RANDOM_SEED); 00087 00088 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) 00089 random_table[i] = rand(); 00090 00091 for (int i = 0; i < RANDOM_TABLE_SIZE; i++) { 00092 int tmp; 00093 int swap = i + rand() % (RANDOM_TABLE_SIZE - i); 00094 tmp = random_table[i]; 00095 random_table[i] = random_table[swap]; 00096 random_table[swap] = tmp; 00097 } 00098 } 00099 00100 inline 00101 int XCFImageFormat::add_lut( int a, int b ) { 00102 return qMin( a + b, 255 ); 00103 } 00104 00105 bool XCFImageFormat::readXCF(QIODevice *device, QImage *outImage) 00106 { 00107 XCFImage xcf_image; 00108 QDataStream xcf_io(device); 00109 00110 char tag[14];; 00111 00112 if (xcf_io.readRawData(tag, sizeof(tag)) != sizeof(tag)) { 00113 kDebug(399) << "XCF: read failure on header tag"; 00114 return false; 00115 } 00116 if (qstrncmp(tag, "gimp xcf", 8) != 0) { 00117 kDebug(399) << "XCF: read called on non-XCF file"; 00118 return false; 00119 } 00120 00121 xcf_io >> xcf_image.width >> xcf_image.height >> xcf_image.type; 00122 00123 kDebug() << tag << " " << xcf_image.width << " " << xcf_image.height << " " << xcf_image.type; 00124 if (!loadImageProperties(xcf_io, xcf_image)) 00125 return false; 00126 00127 // The layers appear to be stored in top-to-bottom order. This is 00128 // the reverse of how a merged image must be computed. So, the layer 00129 // offsets are pushed onto a LIFO stack (thus, we don't have to load 00130 // all the data of all layers before beginning to construct the 00131 // merged image). 00132 00133 QStack<qint32> layer_offsets; 00134 00135 while (true) { 00136 qint32 layer_offset; 00137 00138 xcf_io >> layer_offset; 00139 00140 if (layer_offset == 0) 00141 break; 00142 00143 layer_offsets.push(layer_offset); 00144 } 00145 00146 xcf_image.num_layers = layer_offsets.size(); 00147 00148 if (layer_offsets.size() == 0) { 00149 kDebug(399) << "XCF: no layers!"; 00150 return false; 00151 } 00152 00153 // Load each layer and add it to the image 00154 while (!layer_offsets.isEmpty()) { 00155 qint32 layer_offset = layer_offsets.pop(); 00156 00157 xcf_io.device()->seek(layer_offset); 00158 00159 if (!loadLayer(xcf_io, xcf_image)) 00160 return false; 00161 } 00162 00163 if (!xcf_image.initialized) { 00164 kDebug(399) << "XCF: no visible layers!"; 00165 return false; 00166 } 00167 00168 *outImage = xcf_image.image; 00169 return true; 00170 } 00171 00172 00180 bool XCFImageFormat::loadImageProperties(QDataStream& xcf_io, XCFImage& xcf_image) 00181 { 00182 while (true) { 00183 PropType type; 00184 QByteArray bytes; 00185 00186 if (!loadProperty(xcf_io, type, bytes)) { 00187 kDebug(399) << "XCF: error loading global image properties"; 00188 return false; 00189 } 00190 00191 QDataStream property(bytes); 00192 00193 switch (type) { 00194 case PROP_END: 00195 return true; 00196 00197 case PROP_COMPRESSION: 00198 property >> xcf_image.compression; 00199 break; 00200 00201 case PROP_RESOLUTION: 00202 property >> xcf_image.x_resolution >> xcf_image.y_resolution; 00203 break; 00204 00205 case PROP_TATTOO: 00206 property >> xcf_image.tattoo; 00207 break; 00208 00209 case PROP_PARASITES: 00210 while (!property.atEnd()) { 00211 char* tag; 00212 quint32 size; 00213 00214 property.readBytes(tag, size); 00215 00216 quint32 flags; 00217 char* data=0; 00218 property >> flags >> data; 00219 00220 if (tag && strncmp(tag, "gimp-comment", strlen("gimp-comment")) == 0) 00221 xcf_image.image.setText("Comment", 0, data); 00222 00223 delete[] tag; 00224 delete[] data; 00225 } 00226 break; 00227 00228 case PROP_UNIT: 00229 property >> xcf_image.unit; 00230 break; 00231 00232 case PROP_PATHS: // This property is ignored. 00233 break; 00234 00235 case PROP_USER_UNIT: // This property is ignored. 00236 break; 00237 00238 case PROP_COLORMAP: 00239 property >> xcf_image.num_colors; 00240 if(xcf_image.num_colors < 0 || xcf_image.num_colors > 65535) 00241 return false; 00242 00243 xcf_image.palette.reserve(xcf_image.num_colors); 00244 00245 for (int i = 0; i < xcf_image.num_colors; i++) { 00246 uchar r, g, b; 00247 property >> r >> g >> b; 00248 xcf_image.palette.push_back( qRgb(r,g,b) ); 00249 } 00250 break; 00251 00252 default: 00253 kDebug(399) << "XCF: unimplemented image property" << type 00254 << ", size " << bytes.size() << endl; 00255 } 00256 } 00257 } 00258 00259 00267 bool XCFImageFormat::loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes) 00268 { 00269 quint32 foo; 00270 xcf_io >> foo; 00271 type=PropType(foo); // TODO urks 00272 00273 char* data = 0; 00274 quint32 size; 00275 00276 // The colormap property size is not the correct number of bytes: 00277 // The GIMP source xcf.c has size = 4 + ncolors, but it should be 00278 // 4 + 3 * ncolors 00279 00280 if (type == PROP_COLORMAP) { 00281 xcf_io >> size; 00282 quint32 ncolors; 00283 xcf_io >> ncolors; 00284 00285 if(size > 65535 || size < 4) 00286 return false; 00287 00288 size = 3 * ncolors + 4; 00289 data = new char[size]; 00290 00291 // since we already read "ncolors" from the stream, we put that data back 00292 data[0] = 0; 00293 data[1] = 0; 00294 data[2] = ncolors >> 8; 00295 data[3] = ncolors & 255; 00296 00297 // ... and read the remaining bytes from the stream 00298 xcf_io.readRawData(data + 4, size - 4); 00299 } else if (type == PROP_USER_UNIT) { 00300 // The USER UNIT property size is not correct. I'm not sure why, though. 00301 float factor; 00302 qint32 digits; 00303 00304 xcf_io >> size >> factor >> digits; 00305 00306 for (int i = 0; i < 5; i++) { 00307 char* unit_strings; 00308 00309 xcf_io >> unit_strings; 00310 00311 delete[] unit_strings; 00312 00313 if (xcf_io.device()->atEnd()) { 00314 kDebug(399) << "XCF: read failure on property " << type; 00315 return false; 00316 } 00317 } 00318 00319 size = 0; 00320 } else { 00321 xcf_io >> size; 00322 if(size >256000) 00323 return false; 00324 data = new char[size]; 00325 xcf_io.readRawData(data, size); 00326 } 00327 00328 if (size != 0 && data) 00329 bytes = QByteArray(data,size); 00330 00331 delete [] data; 00332 00333 return true; 00334 } 00335 00336 00345 bool XCFImageFormat::loadLayer(QDataStream& xcf_io, XCFImage& xcf_image) 00346 { 00347 Layer& layer(xcf_image.layer); 00348 delete[] layer.name; 00349 00350 xcf_io >> layer.width >> layer.height >> layer.type >> layer.name; 00351 00352 if (!loadLayerProperties(xcf_io, layer)) 00353 return false; 00354 #if 0 00355 cout << "layer: \"" << layer.name << "\", size: " << layer.width << " x " 00356 << layer.height << ", type: " << layer.type << ", mode: " << layer.mode 00357 << ", opacity: " << layer.opacity << ", visible: " << layer.visible 00358 << ", offset: " << layer.x_offset << ", " << layer.y_offset << endl; 00359 #endif 00360 // Skip reading the rest of it if it is not visible. Typically, when 00361 // you export an image from the The GIMP it flattens (or merges) only 00362 // the visible layers into the output image. 00363 00364 if (layer.visible == 0) 00365 return true; 00366 00367 // If there are any more layers, merge them into the final QImage. 00368 00369 xcf_io >> layer.hierarchy_offset >> layer.mask_offset; 00370 00371 // Allocate the individual tile QImages based on the size and type 00372 // of this layer. 00373 00374 if( !composeTiles(xcf_image)) 00375 return false; 00376 xcf_io.device()->seek(layer.hierarchy_offset); 00377 00378 // As tiles are loaded, they are copied into the layers tiles by 00379 // this routine. (loadMask(), below, uses a slightly different 00380 // version of assignBytes().) 00381 00382 layer.assignBytes = assignImageBytes; 00383 00384 if (!loadHierarchy(xcf_io, layer)) 00385 return false; 00386 00387 if (layer.mask_offset != 0) { 00388 xcf_io.device()->seek(layer.mask_offset); 00389 00390 if (!loadMask(xcf_io, layer)) 00391 return false; 00392 } 00393 00394 // Now we should have enough information to initialize the final 00395 // QImage. The first visible layer determines the attributes 00396 // of the QImage. 00397 00398 if (!xcf_image.initialized) { 00399 if( !initializeImage(xcf_image)) 00400 return false; 00401 copyLayerToImage(xcf_image); 00402 xcf_image.initialized = true; 00403 } else 00404 mergeLayerIntoImage(xcf_image); 00405 00406 return true; 00407 } 00408 00409 00417 bool XCFImageFormat::loadLayerProperties(QDataStream& xcf_io, Layer& layer) 00418 { 00419 while (true) { 00420 PropType type; 00421 QByteArray bytes; 00422 00423 if (!loadProperty(xcf_io, type, bytes)) { 00424 kDebug(399) << "XCF: error loading layer properties"; 00425 return false; 00426 } 00427 00428 QDataStream property(bytes); 00429 00430 switch (type) { 00431 case PROP_END: 00432 return true; 00433 00434 case PROP_ACTIVE_LAYER: 00435 layer.active = true; 00436 break; 00437 00438 case PROP_OPACITY: 00439 property >> layer.opacity; 00440 break; 00441 00442 case PROP_VISIBLE: 00443 property >> layer.visible; 00444 break; 00445 00446 case PROP_LINKED: 00447 property >> layer.linked; 00448 break; 00449 00450 case PROP_PRESERVE_TRANSPARENCY: 00451 property >> layer.preserve_transparency; 00452 break; 00453 00454 case PROP_APPLY_MASK: 00455 property >> layer.apply_mask; 00456 break; 00457 00458 case PROP_EDIT_MASK: 00459 property >> layer.edit_mask; 00460 break; 00461 00462 case PROP_SHOW_MASK: 00463 property >> layer.show_mask; 00464 break; 00465 00466 case PROP_OFFSETS: 00467 property >> layer.x_offset >> layer.y_offset; 00468 break; 00469 00470 case PROP_MODE: 00471 property >> layer.mode; 00472 break; 00473 00474 case PROP_TATTOO: 00475 property >> layer.tattoo; 00476 break; 00477 00478 default: 00479 kDebug(399) << "XCF: unimplemented layer property " << type 00480 << ", size " << bytes.size() << endl; 00481 } 00482 } 00483 } 00484 00485 00491 bool XCFImageFormat::composeTiles(XCFImage& xcf_image) 00492 { 00493 Layer& layer(xcf_image.layer); 00494 00495 layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT; 00496 layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH; 00497 00498 //kDebug(399) << "IMAGE: height=" << xcf_image.height << ", width=" << xcf_image.width; 00499 //kDebug(399) << "LAYER: height=" << layer.height << ", width=" << layer.width; 00500 //kDebug(399) << "LAYER: rows=" << layer.nrows << ", columns=" << layer.ncols; 00501 00502 // SANITY CHECK: Catch corrupted XCF image file where the width or height 00503 // of a tile is reported are bogus. See Bug# 234030. 00504 if (layer.height > xcf_image.height || layer.width > xcf_image.width) 00505 return false; 00506 00507 layer.image_tiles.resize(layer.nrows); 00508 00509 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) 00510 layer.alpha_tiles.resize(layer.nrows); 00511 00512 if (layer.mask_offset != 0) 00513 layer.mask_tiles.resize(layer.nrows); 00514 00515 for (uint j = 0; j < layer.nrows; j++) { 00516 layer.image_tiles[j].resize(layer.ncols); 00517 00518 if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) 00519 layer.alpha_tiles[j].resize(layer.ncols); 00520 00521 if (layer.mask_offset != 0) 00522 layer.mask_tiles[j].resize(layer.ncols); 00523 } 00524 00525 for (uint j = 0; j < layer.nrows; j++) { 00526 for (uint i = 0; i < layer.ncols; i++) { 00527 00528 uint tile_width = (i + 1) * TILE_WIDTH <= layer.width 00529 ? TILE_WIDTH : layer.width - i * TILE_WIDTH; 00530 00531 uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height 00532 ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT; 00533 00534 // Try to create the most appropriate QImage (each GIMP layer 00535 // type is treated slightly differently) 00536 00537 switch (layer.type) { 00538 case RGB_GIMAGE: 00539 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_RGB32); 00540 layer.image_tiles[j][i].setNumColors(0); 00541 if( layer.image_tiles[j][i].isNull()) 00542 return false; 00543 break; 00544 00545 case RGBA_GIMAGE: 00546 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_ARGB32); 00547 layer.image_tiles[j][i].setNumColors(0); 00548 if( layer.image_tiles[j][i].isNull()) 00549 return false; 00550 break; 00551 00552 case GRAY_GIMAGE: 00553 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00554 layer.image_tiles[j][i].setNumColors(256); 00555 if( layer.image_tiles[j][i].isNull()) 00556 return false; 00557 setGrayPalette(layer.image_tiles[j][i]); 00558 break; 00559 00560 case GRAYA_GIMAGE: 00561 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00562 layer.image_tiles[j][i].setNumColors(256); 00563 if( layer.image_tiles[j][i].isNull()) 00564 return false; 00565 setGrayPalette(layer.image_tiles[j][i]); 00566 00567 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00568 layer.alpha_tiles[j][i].setNumColors(256); 00569 if( layer.alpha_tiles[j][i].isNull()) 00570 return false; 00571 setGrayPalette(layer.alpha_tiles[j][i]); 00572 break; 00573 00574 case INDEXED_GIMAGE: 00575 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00576 layer.image_tiles[j][i].setNumColors(xcf_image.num_colors); 00577 if( layer.image_tiles[j][i].isNull()) 00578 return false; 00579 setPalette(xcf_image, layer.image_tiles[j][i]); 00580 break; 00581 00582 case INDEXEDA_GIMAGE: 00583 layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00584 layer.image_tiles[j][i].setNumColors(xcf_image.num_colors); 00585 if( layer.image_tiles[j][i].isNull()) 00586 return false; 00587 setPalette(xcf_image, layer.image_tiles[j][i]); 00588 00589 layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00590 layer.alpha_tiles[j][i].setNumColors(256); 00591 if( layer.alpha_tiles[j][i].isNull()) 00592 return false; 00593 setGrayPalette(layer.alpha_tiles[j][i]); 00594 } 00595 00596 if (layer.mask_offset != 0) { 00597 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); 00598 layer.mask_tiles[j][i].setNumColors(256); 00599 if( layer.mask_tiles[j][i].isNull()) 00600 return false; 00601 setGrayPalette(layer.mask_tiles[j][i]); 00602 } 00603 } 00604 } 00605 return true; 00606 } 00607 00608 00615 void XCFImageFormat::setGrayPalette(QImage& image) 00616 { 00617 if (grayTable.isEmpty()) { 00618 grayTable.resize(256); 00619 00620 for (int i = 0; i < 256; i++) 00621 grayTable[i] = qRgb(i, i, i); 00622 } 00623 00624 image.setColorTable(grayTable); 00625 } 00626 00627 00633 void XCFImageFormat::setPalette(XCFImage& xcf_image, QImage& image) 00634 { 00635 Q_ASSERT (xcf_image.num_colors == xcf_image.palette.size()); 00636 00637 image.setColorTable(xcf_image.palette); 00638 } 00639 00640 00648 void XCFImageFormat::assignImageBytes(Layer& layer, uint i, uint j) 00649 { 00650 QImage &image = layer.image_tiles[j][i]; 00651 uchar* tile = layer.tile; 00652 const int width = image.width(); 00653 const int height = image.height(); 00654 const int bytesPerLine = image.bytesPerLine(); 00655 uchar *bits = image.bits(); 00656 00657 switch (layer.type) { 00658 case RGB_GIMAGE: 00659 for (int y = 0; y < height; y++) { 00660 QRgb *dataPtr = (QRgb *) (bits + y * bytesPerLine); 00661 for (int x = 0; x < width; x++) { 00662 *dataPtr++ = qRgb(tile[0], tile[1], tile[2]); 00663 tile += sizeof(QRgb); 00664 } 00665 } 00666 break; 00667 00668 case RGBA_GIMAGE: 00669 for (int y = 0; y < height; y++) { 00670 QRgb *dataPtr = (QRgb *) (bits + y * bytesPerLine); 00671 for (int x = 0; x < width; x++) { 00672 *dataPtr++ = qRgba(tile[0], tile[1], tile[2], tile[3]); 00673 tile += sizeof(QRgb); 00674 } 00675 } 00676 break; 00677 00678 case GRAY_GIMAGE: 00679 case INDEXED_GIMAGE: 00680 for (int y = 0; y < height; y++) { 00681 uchar *dataPtr = bits + y * bytesPerLine; 00682 for (int x = 0; x < width; x++) { 00683 *dataPtr++ = tile[0]; 00684 tile += sizeof(QRgb); 00685 } 00686 } 00687 break; 00688 00689 case GRAYA_GIMAGE: 00690 case INDEXEDA_GIMAGE: 00691 for (int y = 0; y < height; y++) { 00692 uchar *dataPtr = bits + y * bytesPerLine; 00693 uchar *alphaPtr = layer.alpha_tiles[j][i].scanLine(y); 00694 for (int x = 0; x < width; x++) { 00695 00696 // The "if" here should not be necessary, but apparently there 00697 // are some cases where the image can contain larger indices 00698 // than there are colors in the palette. (A bug in The GIMP?) 00699 00700 if (tile[0] < image.numColors()) 00701 *dataPtr = tile[0]; 00702 00703 *alphaPtr = tile[1]; 00704 dataPtr += 1; 00705 alphaPtr += 1; 00706 tile += sizeof(QRgb); 00707 } 00708 } 00709 break; 00710 } 00711 } 00712 00713 00722 bool XCFImageFormat::loadHierarchy(QDataStream& xcf_io, Layer& layer) 00723 { 00724 qint32 width; 00725 qint32 height; 00726 qint32 bpp; 00727 quint32 offset; 00728 00729 xcf_io >> width >> height >> bpp >> offset; 00730 00731 // GIMP stores images in a "mipmap"-like format (multiple levels of 00732 // increasingly lower resolution). Only the top level is used here, 00733 // however. 00734 00735 quint32 junk; 00736 do { 00737 xcf_io >> junk; 00738 00739 if (xcf_io.device()->atEnd()) { 00740 kDebug(399) << "XCF: read failure on layer " << layer.name << " level offsets"; 00741 return false; 00742 } 00743 } while (junk != 0); 00744 00745 qint64 saved_pos = xcf_io.device()->pos(); 00746 00747 xcf_io.device()->seek(offset); 00748 if (!loadLevel(xcf_io, layer, bpp)) 00749 return false; 00750 00751 xcf_io.device()->seek(saved_pos); 00752 return true; 00753 } 00754 00755 00764 bool XCFImageFormat::loadLevel(QDataStream& xcf_io, Layer& layer, qint32 bpp) 00765 { 00766 qint32 width; 00767 qint32 height; 00768 quint32 offset; 00769 00770 xcf_io >> width >> height >> offset; 00771 00772 if (offset == 0) 00773 return true; 00774 00775 for (uint j = 0; j < layer.nrows; j++) { 00776 for (uint i = 0; i < layer.ncols; i++) { 00777 00778 if (offset == 0) { 00779 kDebug(399) << "XCF: incorrect number of tiles in layer " << layer.name; 00780 return false; 00781 } 00782 00783 qint64 saved_pos = xcf_io.device()->pos(); 00784 quint32 offset2; 00785 xcf_io >> offset2; 00786 00787 // Evidently, RLE can occasionally expand a tile instead of compressing it! 00788 00789 if (offset2 == 0) 00790 offset2 = offset + (uint)(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5); 00791 00792 xcf_io.device()->seek(offset); 00793 int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height(); 00794 00795 if (!loadTileRLE(xcf_io, layer.tile, size, offset2 - offset, bpp)) 00796 return false; 00797 00798 // The bytes in the layer tile are juggled differently depending on 00799 // the target QImage. The caller has set layer.assignBytes to the 00800 // appropriate routine. 00801 00802 layer.assignBytes(layer, i, j); 00803 00804 xcf_io.device()->seek(saved_pos); 00805 xcf_io >> offset; 00806 } 00807 } 00808 00809 return true; 00810 } 00811 00812 00819 bool XCFImageFormat::loadMask(QDataStream& xcf_io, Layer& layer) 00820 { 00821 qint32 width; 00822 qint32 height; 00823 char* name; 00824 00825 xcf_io >> width >> height >> name; 00826 00827 delete name; 00828 00829 if (!loadChannelProperties(xcf_io, layer)) 00830 return false; 00831 00832 quint32 hierarchy_offset; 00833 xcf_io >> hierarchy_offset; 00834 00835 xcf_io.device()->seek(hierarchy_offset); 00836 layer.assignBytes = assignMaskBytes; 00837 00838 if (!loadHierarchy(xcf_io, layer)) 00839 return false; 00840 00841 return true; 00842 } 00843 00844 00868 bool XCFImageFormat::loadTileRLE(QDataStream& xcf_io, uchar* tile, int image_size, 00869 int data_length, qint32 bpp) 00870 { 00871 uchar* data; 00872 00873 uchar* xcfdata; 00874 uchar* xcfodata; 00875 uchar* xcfdatalimit; 00876 00877 if (data_length < 0 || data_length > int(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5)) { 00878 kDebug(399) << "XCF: invalid tile data length" << data_length; 00879 return false; 00880 } 00881 00882 xcfdata = xcfodata = new uchar[data_length]; 00883 00884 xcf_io.readRawData((char*)xcfdata, data_length); 00885 00886 if (!xcf_io.device()->isOpen()) { 00887 delete[] xcfodata; 00888 kDebug(399) << "XCF: read failure on tile"; 00889 return false; 00890 } 00891 00892 xcfdatalimit = &xcfodata[data_length - 1]; 00893 00894 for (int i = 0; i < bpp; ++i) { 00895 00896 data = tile + i; 00897 00898 int count = 0; 00899 int size = image_size; 00900 00901 while (size > 0) { 00902 if (xcfdata > xcfdatalimit) 00903 goto bogus_rle; 00904 00905 uchar val = *xcfdata++; 00906 uint length = val; 00907 00908 if (length >= 128) { 00909 length = 255 - (length - 1); 00910 if (length == 128) { 00911 if (xcfdata >= xcfdatalimit) 00912 goto bogus_rle; 00913 00914 length = (*xcfdata << 8) + xcfdata[1]; 00915 00916 xcfdata += 2; 00917 } 00918 00919 count += length; 00920 size -= length; 00921 00922 if (size < 0) 00923 goto bogus_rle; 00924 00925 if (&xcfdata[length - 1] > xcfdatalimit) 00926 goto bogus_rle; 00927 00928 while (length-- > 0) { 00929 *data = *xcfdata++; 00930 data += sizeof(QRgb); 00931 } 00932 } else { 00933 length += 1; 00934 if (length == 128) { 00935 if (xcfdata >= xcfdatalimit) 00936 goto bogus_rle; 00937 00938 length = (*xcfdata << 8) + xcfdata[1]; 00939 xcfdata += 2; 00940 } 00941 00942 count += length; 00943 size -= length; 00944 00945 if (size < 0) 00946 goto bogus_rle; 00947 00948 if (xcfdata > xcfdatalimit) 00949 goto bogus_rle; 00950 00951 val = *xcfdata++; 00952 00953 while (length-- > 0) { 00954 *data = val; 00955 data += sizeof(QRgb); 00956 } 00957 } 00958 } 00959 } 00960 00961 delete[] xcfodata; 00962 return true; 00963 00964 bogus_rle: 00965 00966 kDebug(399) << "The run length encoding could not be decoded properly"; 00967 delete[] xcfodata; 00968 return false; 00969 } 00970 00971 00979 bool XCFImageFormat::loadChannelProperties(QDataStream& xcf_io, Layer& layer) 00980 { 00981 while (true) { 00982 PropType type; 00983 QByteArray bytes; 00984 00985 if (!loadProperty(xcf_io, type, bytes)) { 00986 kDebug(399) << "XCF: error loading channel properties"; 00987 return false; 00988 } 00989 00990 QDataStream property(bytes); 00991 00992 switch (type) { 00993 case PROP_END: 00994 return true; 00995 00996 case PROP_OPACITY: 00997 property >> layer.mask_channel.opacity; 00998 break; 00999 01000 case PROP_VISIBLE: 01001 property >> layer.mask_channel.visible; 01002 break; 01003 01004 case PROP_SHOW_MASKED: 01005 property >> layer.mask_channel.show_masked; 01006 break; 01007 01008 case PROP_COLOR: 01009 property >> layer.mask_channel.red >> layer.mask_channel.green 01010 >> layer.mask_channel.blue; 01011 break; 01012 01013 case PROP_TATTOO: 01014 property >> layer.mask_channel.tattoo; 01015 break; 01016 01017 default: 01018 kDebug(399) << "XCF: unimplemented channel property " << type 01019 << ", size " << bytes.size() << endl; 01020 } 01021 } 01022 } 01023 01024 01031 void XCFImageFormat::assignMaskBytes(Layer& layer, uint i, uint j) 01032 { 01033 QImage &image = layer.mask_tiles[j][i]; 01034 uchar* tile = layer.tile; 01035 const int width = image.width(); 01036 const int height = image.height(); 01037 const int bytesPerLine = image.bytesPerLine(); 01038 uchar *bits = image.bits(); 01039 01040 for (int y = 0; y < height; y++) { 01041 uchar *dataPtr = bits + y * bytesPerLine; 01042 for (int x = 0; x < width; x++) { 01043 *dataPtr++ = tile[0]; 01044 tile += sizeof(QRgb); 01045 } 01046 } 01047 } 01048 01049 01078 bool XCFImageFormat::initializeImage(XCFImage& xcf_image) 01079 { 01080 // (Aliases to make the code look a little better.) 01081 Layer& layer(xcf_image.layer); 01082 QImage& image(xcf_image.image); 01083 01084 switch (layer.type) { 01085 case RGB_GIMAGE: 01086 if (layer.opacity == OPAQUE_OPACITY) { 01087 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_RGB32); 01088 if( image.isNull()) 01089 return false; 01090 image.fill(qRgb(255, 255, 255)); 01091 break; 01092 } // else, fall through to 32-bit representation 01093 01094 case RGBA_GIMAGE: 01095 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); 01096 if( image.isNull()) 01097 return false; 01098 image.fill(qRgba(255, 255, 255, 0)); 01099 break; 01100 01101 case GRAY_GIMAGE: 01102 if (layer.opacity == OPAQUE_OPACITY) { 01103 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8); 01104 image.setNumColors(256); 01105 if( image.isNull()) 01106 return false; 01107 setGrayPalette(image); 01108 image.fill(255); 01109 break; 01110 } // else, fall through to 32-bit representation 01111 01112 case GRAYA_GIMAGE: 01113 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); 01114 if( image.isNull()) 01115 return false; 01116 image.fill(qRgba(255, 255, 255, 0)); 01117 break; 01118 01119 case INDEXED_GIMAGE: 01120 // As noted in the table above, there are quite a few combinations 01121 // which are possible with indexed images, depending on the 01122 // presence of transparency (note: not translucency, which is not 01123 // supported by The GIMP for indexed images) and the number of 01124 // individual colors. 01125 01126 // Note: Qt treats a bitmap with a Black and White color palette 01127 // as a mask, so only the "on" bits are drawn, regardless of the 01128 // order color table entries. Otherwise (i.e., at least one of the 01129 // color table entries is not black or white), it obeys the one- 01130 // or two-color palette. Have to ask about this... 01131 01132 if (xcf_image.num_colors <= 2) { 01133 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB); 01134 image.setNumColors(xcf_image.num_colors); 01135 if( image.isNull()) 01136 return false; 01137 image.fill(0); 01138 setPalette(xcf_image, image); 01139 } else if (xcf_image.num_colors <= 256) { 01140 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8); 01141 image.setNumColors(xcf_image.num_colors); 01142 if( image.isNull()) 01143 return false; 01144 image.fill(0); 01145 setPalette(xcf_image, image); 01146 } 01147 break; 01148 01149 case INDEXEDA_GIMAGE: 01150 if (xcf_image.num_colors == 1) { 01151 // Plenty(!) of room to add a transparent color 01152 xcf_image.num_colors++; 01153 xcf_image.palette.resize(xcf_image.num_colors); 01154 xcf_image.palette[1] = xcf_image.palette[0]; 01155 xcf_image.palette[0] = qRgba(255, 255, 255, 0); 01156 01157 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB); 01158 image.setNumColors(xcf_image.num_colors); 01159 if( image.isNull()) 01160 return false; 01161 image.fill(0); 01162 setPalette(xcf_image, image); 01163 } else if (xcf_image.num_colors < 256) { 01164 // Plenty of room to add a transparent color 01165 xcf_image.num_colors++; 01166 xcf_image.palette.resize(xcf_image.num_colors); 01167 for (int c = xcf_image.num_colors - 1; c >= 1; c--) 01168 xcf_image.palette[c] = xcf_image.palette[c - 1]; 01169 01170 xcf_image.palette[0] = qRgba(255, 255, 255, 0); 01171 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_Indexed8); 01172 image.setNumColors(xcf_image.num_colors); 01173 if( image.isNull()) 01174 return false; 01175 image.fill(0); 01176 setPalette(xcf_image, image); 01177 } else { 01178 // No room for a transparent color, so this has to be promoted to 01179 // true color. (There is no equivalent PNG representation output 01180 // from The GIMP as of v1.2.) 01181 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); 01182 if( image.isNull()) 01183 return false; 01184 image.fill(qRgba(255, 255, 255, 0)); 01185 } 01186 break; 01187 } 01188 01189 image.setDotsPerMeterX((int)(xcf_image.x_resolution * INCHESPERMETER)); 01190 image.setDotsPerMeterY((int)(xcf_image.y_resolution * INCHESPERMETER)); 01191 return true; 01192 } 01193 01194 01200 void XCFImageFormat::copyLayerToImage(XCFImage& xcf_image) 01201 { 01202 Layer& layer(xcf_image.layer); 01203 QImage& image(xcf_image.image); 01204 PixelCopyOperation copy = 0; 01205 01206 switch (layer.type) { 01207 case RGB_GIMAGE: 01208 case RGBA_GIMAGE: 01209 copy = copyRGBToRGB; 01210 break; 01211 case GRAY_GIMAGE: 01212 if (layer.opacity == OPAQUE_OPACITY) 01213 copy = copyGrayToGray; 01214 else 01215 copy = copyGrayToRGB; 01216 break; 01217 case GRAYA_GIMAGE: 01218 copy = copyGrayAToRGB; 01219 break; 01220 case INDEXED_GIMAGE: 01221 copy = copyIndexedToIndexed; 01222 break; 01223 case INDEXEDA_GIMAGE: 01224 if (xcf_image.image.depth() <= 8) 01225 copy = copyIndexedAToIndexed; 01226 else 01227 copy = copyIndexedAToRGB; 01228 } 01229 01230 if (!copy) { 01231 return; 01232 } 01233 01234 // For each tile... 01235 01236 for (uint j = 0; j < layer.nrows; j++) { 01237 uint y = j * TILE_HEIGHT; 01238 01239 for (uint i = 0; i < layer.ncols; i++) { 01240 uint x = i * TILE_WIDTH; 01241 01242 // This seems the best place to apply the dissolve because it 01243 // depends on the global position of each tile's 01244 // pixels. Apparently it's the only mode which can apply to a 01245 // single layer. 01246 01247 if (layer.mode == DISSOLVE_MODE) { 01248 if (!random_table_initialized) { 01249 initializeRandomTable(); 01250 random_table_initialized = true; 01251 } 01252 if (layer.type == RGBA_GIMAGE) 01253 dissolveRGBPixels(layer.image_tiles[j][i], x, y); 01254 01255 else if (layer.type == GRAYA_GIMAGE) 01256 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y); 01257 } 01258 01259 // Shortcut for common case 01260 if (copy == copyRGBToRGB && layer.apply_mask != 1) { 01261 QPainter painter(&image); 01262 painter.setOpacity(layer.opacity / 255.0); 01263 painter.setCompositionMode(QPainter::CompositionMode_Source); 01264 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]); 01265 continue; 01266 } 01267 01268 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) { 01269 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) { 01270 01271 int m = x + k + layer.x_offset; 01272 int n = y + l + layer.y_offset; 01273 01274 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) 01275 continue; 01276 01277 (*copy)(layer, i, j, k, l, image, m, n); 01278 } 01279 } 01280 } 01281 } 01282 } 01283 01284 01298 void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l, 01299 QImage& image, int m, int n) 01300 { 01301 QRgb src = layer.image_tiles[j][i].pixel(k, l); 01302 uchar src_a = layer.opacity; 01303 01304 if (layer.type == RGBA_GIMAGE) 01305 src_a = INT_MULT(src_a, qAlpha(src)); 01306 01307 // Apply the mask (if any) 01308 01309 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 01310 layer.mask_tiles[j].size() > (int)i) 01311 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 01312 01313 image.setPixel(m, n, qRgba(src, src_a)); 01314 } 01315 01316 01328 void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l, 01329 QImage& image, int m, int n) 01330 { 01331 int src = layer.image_tiles[j][i].pixelIndex(k, l); 01332 image.setPixel(m, n, src); 01333 } 01334 01335 01349 void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l, 01350 QImage& image, int m, int n) 01351 { 01352 QRgb src = layer.image_tiles[j][i].pixel(k, l); 01353 uchar src_a = layer.opacity; 01354 image.setPixel(m, n, qRgba(src, src_a)); 01355 } 01356 01357 01371 void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l, 01372 QImage& image, int m, int n) 01373 { 01374 QRgb src = layer.image_tiles[j][i].pixel(k, l); 01375 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 01376 src_a = INT_MULT(src_a, layer.opacity); 01377 01378 // Apply the mask (if any) 01379 01380 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 01381 layer.mask_tiles[j].size() > (int)i) 01382 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 01383 01384 image.setPixel(m, n, qRgba(src, src_a)); 01385 } 01386 01387 01399 void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l, 01400 QImage& image, int m, int n) 01401 { 01402 int src = layer.image_tiles[j][i].pixelIndex(k, l); 01403 image.setPixel(m, n, src); 01404 } 01405 01406 01418 void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l, 01419 QImage& image, int m, int n) 01420 { 01421 uchar src = layer.image_tiles[j][i].pixelIndex(k, l); 01422 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 01423 src_a = INT_MULT(src_a, layer.opacity); 01424 01425 if (layer.apply_mask == 1 && 01426 layer.mask_tiles.size() > (int)j && 01427 layer.mask_tiles[j].size() > (int)i) 01428 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 01429 01430 if (src_a > 127) 01431 src++; 01432 else 01433 src = 0; 01434 01435 image.setPixel(m, n, src); 01436 } 01437 01438 01452 void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l, 01453 QImage& image, int m, int n) 01454 { 01455 QRgb src = layer.image_tiles[j][i].pixel(k, l); 01456 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 01457 src_a = INT_MULT(src_a, layer.opacity); 01458 01459 // Apply the mask (if any) 01460 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 01461 layer.mask_tiles[j].size() > (int)i) 01462 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 01463 01464 // This is what appears in the GIMP window 01465 if (src_a <= 127) 01466 src_a = 0; 01467 else 01468 src_a = OPAQUE_OPACITY; 01469 01470 image.setPixel(m, n, qRgba(src, src_a)); 01471 } 01472 01473 01478 void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image) 01479 { 01480 Layer& layer(xcf_image.layer); 01481 QImage& image(xcf_image.image); 01482 01483 PixelMergeOperation merge = 0; 01484 01485 if (!layer.opacity) return; // don't bother doing anything 01486 01487 switch (layer.type) { 01488 case RGB_GIMAGE: 01489 case RGBA_GIMAGE: 01490 merge = mergeRGBToRGB; 01491 break; 01492 case GRAY_GIMAGE: 01493 if (layer.opacity == OPAQUE_OPACITY) 01494 merge = mergeGrayToGray; 01495 else 01496 merge = mergeGrayToRGB; 01497 break; 01498 case GRAYA_GIMAGE: 01499 if (xcf_image.image.depth() <= 8) 01500 merge = mergeGrayAToGray; 01501 else 01502 merge = mergeGrayAToRGB; 01503 break; 01504 case INDEXED_GIMAGE: 01505 merge = mergeIndexedToIndexed; 01506 break; 01507 case INDEXEDA_GIMAGE: 01508 if (xcf_image.image.depth() <= 8) 01509 merge = mergeIndexedAToIndexed; 01510 else 01511 merge = mergeIndexedAToRGB; 01512 } 01513 01514 if (!merge) { 01515 return; 01516 } 01517 01518 for (uint j = 0; j < layer.nrows; j++) { 01519 uint y = j * TILE_HEIGHT; 01520 01521 for (uint i = 0; i < layer.ncols; i++) { 01522 uint x = i * TILE_WIDTH; 01523 01524 // This seems the best place to apply the dissolve because it 01525 // depends on the global position of each tile's 01526 // pixels. Apparently it's the only mode which can apply to a 01527 // single layer. 01528 01529 if (layer.mode == DISSOLVE_MODE) { 01530 if (!random_table_initialized) { 01531 initializeRandomTable(); 01532 random_table_initialized = true; 01533 } 01534 if (layer.type == RGBA_GIMAGE) 01535 dissolveRGBPixels(layer.image_tiles[j][i], x, y); 01536 01537 else if (layer.type == GRAYA_GIMAGE) 01538 dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y); 01539 } 01540 01541 // Shortcut for common case 01542 if (merge == mergeRGBToRGB && layer.apply_mask != 1 01543 && layer.mode == NORMAL_MODE) { 01544 QPainter painter(&image); 01545 painter.setOpacity(layer.opacity / 255.0); 01546 painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 01547 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]); 01548 continue; 01549 } 01550 01551 for (int l = 0; l < layer.image_tiles[j][i].height(); l++) { 01552 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) { 01553 01554 int m = x + k + layer.x_offset; 01555 int n = y + l + layer.y_offset; 01556 01557 if (m < 0 || m >= image.width() || n < 0 || n >= image.height()) 01558 continue; 01559 01560 (*merge)(layer, i, j, k, l, image, m, n); 01561 } 01562 } 01563 } 01564 } 01565 } 01566 01567 01581 void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l, 01582 QImage& image, int m, int n) 01583 { 01584 QRgb src = layer.image_tiles[j][i].pixel(k, l); 01585 QRgb dst = image.pixel(m, n); 01586 01587 uchar src_r = qRed(src); 01588 uchar src_g = qGreen(src); 01589 uchar src_b = qBlue(src); 01590 uchar src_a = qAlpha(src); 01591 01592 uchar dst_r = qRed(dst); 01593 uchar dst_g = qGreen(dst); 01594 uchar dst_b = qBlue(dst); 01595 uchar dst_a = qAlpha(dst); 01596 01597 if (!src_a) return; // nothing to merge 01598 01599 switch (layer.mode) { 01600 case MULTIPLY_MODE: { 01601 src_r = INT_MULT(src_r, dst_r); 01602 src_g = INT_MULT(src_g, dst_g); 01603 src_b = INT_MULT(src_b, dst_b); 01604 src_a = qMin(src_a, dst_a); 01605 } 01606 break; 01607 case DIVIDE_MODE: { 01608 src_r = qMin((dst_r * 256) / (1 + src_r), 255); 01609 src_g = qMin((dst_g * 256) / (1 + src_g), 255); 01610 src_b = qMin((dst_b * 256) / (1 + src_b), 255); 01611 src_a = qMin(src_a, dst_a); 01612 } 01613 break; 01614 case SCREEN_MODE: { 01615 src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r); 01616 src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g); 01617 src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b); 01618 src_a = qMin(src_a, dst_a); 01619 } 01620 break; 01621 case OVERLAY_MODE: { 01622 src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r)); 01623 src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g)); 01624 src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b)); 01625 src_a = qMin(src_a, dst_a); 01626 } 01627 break; 01628 case DIFFERENCE_MODE: { 01629 src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r; 01630 src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g; 01631 src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b; 01632 src_a = qMin(src_a, dst_a); 01633 } 01634 break; 01635 case ADDITION_MODE: { 01636 src_r = add_lut(dst_r,src_r); 01637 src_g = add_lut(dst_g,src_g); 01638 src_b = add_lut(dst_b,src_b); 01639 src_a = qMin(src_a, dst_a); 01640 } 01641 break; 01642 case SUBTRACT_MODE: { 01643 src_r = dst_r > src_r ? dst_r - src_r : 0; 01644 src_g = dst_g > src_g ? dst_g - src_g : 0; 01645 src_b = dst_b > src_b ? dst_b - src_b : 0; 01646 src_a = qMin(src_a, dst_a); 01647 } 01648 break; 01649 case DARKEN_ONLY_MODE: { 01650 src_r = dst_r < src_r ? dst_r : src_r; 01651 src_g = dst_g < src_g ? dst_g : src_g; 01652 src_b = dst_b < src_b ? dst_b : src_b; 01653 src_a = qMin( src_a, dst_a ); 01654 } 01655 break; 01656 case LIGHTEN_ONLY_MODE: { 01657 src_r = dst_r < src_r ? src_r : dst_r; 01658 src_g = dst_g < src_g ? src_g : dst_g; 01659 src_b = dst_b < src_b ? src_b : dst_b; 01660 src_a = qMin(src_a, dst_a); 01661 } 01662 break; 01663 case HUE_MODE: { 01664 uchar new_r = dst_r; 01665 uchar new_g = dst_g; 01666 uchar new_b = dst_b; 01667 01668 RGBTOHSV(src_r, src_g, src_b); 01669 RGBTOHSV(new_r, new_g, new_b); 01670 01671 new_r = src_r; 01672 01673 HSVTORGB(new_r, new_g, new_b); 01674 01675 src_r = new_r; 01676 src_g = new_g; 01677 src_b = new_b; 01678 src_a = qMin( src_a, dst_a ); 01679 } 01680 break; 01681 case SATURATION_MODE: { 01682 uchar new_r = dst_r; 01683 uchar new_g = dst_g; 01684 uchar new_b = dst_b; 01685 01686 RGBTOHSV(src_r, src_g, src_b); 01687 RGBTOHSV(new_r, new_g, new_b); 01688 01689 new_g = src_g; 01690 01691 HSVTORGB(new_r, new_g, new_b); 01692 01693 src_r = new_r; 01694 src_g = new_g; 01695 src_b = new_b; 01696 src_a = qMin(src_a, dst_a); 01697 } 01698 break; 01699 case VALUE_MODE: { 01700 uchar new_r = dst_r; 01701 uchar new_g = dst_g; 01702 uchar new_b = dst_b; 01703 01704 RGBTOHSV(src_r, src_g, src_b); 01705 RGBTOHSV(new_r, new_g, new_b); 01706 01707 new_b = src_b; 01708 01709 HSVTORGB(new_r, new_g, new_b); 01710 01711 src_r = new_r; 01712 src_g = new_g; 01713 src_b = new_b; 01714 src_a = qMin(src_a, dst_a); 01715 } 01716 break; 01717 case COLOR_MODE: { 01718 uchar new_r = dst_r; 01719 uchar new_g = dst_g; 01720 uchar new_b = dst_b; 01721 01722 RGBTOHLS(src_r, src_g, src_b); 01723 RGBTOHLS(new_r, new_g, new_b); 01724 01725 new_r = src_r; 01726 new_b = src_b; 01727 01728 HLSTORGB(new_r, new_g, new_b); 01729 01730 src_r = new_r; 01731 src_g = new_g; 01732 src_b = new_b; 01733 src_a = qMin(src_a, dst_a); 01734 } 01735 break; 01736 case DODGE_MODE: { 01737 uint tmp; 01738 01739 tmp = dst_r << 8; 01740 tmp /= 256 - src_r; 01741 src_r = (uchar) qMin(tmp, 255u); 01742 01743 tmp = dst_g << 8; 01744 tmp /= 256 - src_g; 01745 src_g = (uchar) qMin(tmp, 255u); 01746 01747 tmp = dst_b << 8; 01748 tmp /= 256 - src_b; 01749 src_b = (uchar) qMin(tmp, 255u); 01750 01751 src_a = qMin(src_a, dst_a); 01752 } 01753 break; 01754 case BURN_MODE: { 01755 uint tmp; 01756 01757 tmp = (255 - dst_r) << 8; 01758 tmp /= src_r + 1; 01759 src_r = (uchar) qMin(tmp, 255u); 01760 src_r = 255 - src_r; 01761 01762 tmp = (255 - dst_g) << 8; 01763 tmp /= src_g + 1; 01764 src_g = (uchar) qMin(tmp, 255u); 01765 src_g = 255 - src_g; 01766 01767 tmp = (255 - dst_b) << 8; 01768 tmp /= src_b + 1; 01769 src_b = (uchar) qMin(tmp, 255u); 01770 src_b = 255 - src_b; 01771 01772 src_a = qMin(src_a, dst_a); 01773 } 01774 break; 01775 case HARDLIGHT_MODE: { 01776 uint tmp; 01777 if (src_r > 128) { 01778 tmp = ((int)255-dst_r) * ((int) 255 - ((src_r-128) << 1)); 01779 src_r = (uchar) qMin(255 - (tmp >> 8), 255u); 01780 } else { 01781 tmp = (int) dst_r * ((int) src_r << 1); 01782 src_r = (uchar) qMin(tmp >> 8, 255u); 01783 } 01784 01785 if (src_g > 128) { 01786 tmp = ((int)255-dst_g) * ((int) 255 - ((src_g-128) << 1)); 01787 src_g = (uchar) qMin(255 - (tmp >> 8), 255u); 01788 } else { 01789 tmp = (int) dst_g * ((int) src_g << 1); 01790 src_g = (uchar) qMin(tmp >> 8, 255u); 01791 } 01792 01793 if (src_b > 128) { 01794 tmp = ((int)255-dst_b) * ((int) 255 - ((src_b-128) << 1)); 01795 src_b = (uchar) qMin(255 - (tmp >> 8), 255u); 01796 } else { 01797 tmp = (int) dst_b * ((int) src_b << 1); 01798 src_b = (uchar) qMin(tmp >> 8, 255u); 01799 } 01800 src_a = qMin(src_a, dst_a); 01801 } 01802 break; 01803 case SOFTLIGHT_MODE: { 01804 uint tmpS, tmpM; 01805 01806 tmpM = INT_MULT(dst_r, src_r); 01807 tmpS = 255 - INT_MULT((255 - dst_r), (255-src_r)); 01808 src_r = INT_MULT((255 - dst_r), tmpM) 01809 + INT_MULT(dst_r, tmpS); 01810 01811 tmpM = INT_MULT(dst_g, src_g); 01812 tmpS = 255 - INT_MULT((255 - dst_g), (255-src_g)); 01813 src_g = INT_MULT((255 - dst_g), tmpM) 01814 + INT_MULT(dst_g, tmpS); 01815 01816 tmpM = INT_MULT(dst_b, src_b); 01817 tmpS = 255 - INT_MULT((255 - dst_b), (255-src_b)); 01818 src_b = INT_MULT((255 - dst_b), tmpM) 01819 + INT_MULT(dst_b, tmpS); 01820 01821 src_a = qMin(src_a, dst_a); 01822 } 01823 break; 01824 case GRAIN_EXTRACT_MODE: { 01825 int tmp; 01826 01827 tmp = dst_r - src_r + 128; 01828 tmp = qMin(tmp, 255); 01829 tmp = qMax(tmp, 0); 01830 src_r = (uchar) tmp; 01831 01832 tmp = dst_g - src_g + 128; 01833 tmp = qMin(tmp, 255); 01834 tmp = qMax(tmp, 0); 01835 src_g = (uchar) tmp; 01836 01837 tmp = dst_b - src_b + 128; 01838 tmp = qMin(tmp, 255); 01839 tmp = qMax(tmp, 0); 01840 src_b = (uchar) tmp; 01841 01842 src_a = qMin(src_a, dst_a); 01843 } 01844 break; 01845 case GRAIN_MERGE_MODE: { 01846 int tmp; 01847 01848 tmp = dst_r + src_r - 128; 01849 tmp = qMin(tmp, 255); 01850 tmp = qMax(tmp, 0); 01851 src_r = (uchar) tmp; 01852 01853 tmp = dst_g + src_g - 128; 01854 tmp = qMin(tmp, 255); 01855 tmp = qMax(tmp, 0); 01856 src_g = (uchar) tmp; 01857 01858 tmp = dst_b + src_b - 128; 01859 tmp = qMin(tmp, 255); 01860 tmp = qMax(tmp, 0); 01861 src_b = (uchar) tmp; 01862 01863 src_a = qMin(src_a, dst_a); 01864 } 01865 break; 01866 } 01867 01868 src_a = INT_MULT(src_a, layer.opacity); 01869 01870 // Apply the mask (if any) 01871 01872 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 01873 layer.mask_tiles[j].size() > (int)i) 01874 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 01875 01876 uchar new_r, new_g, new_b, new_a; 01877 new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a); 01878 01879 float src_ratio = (float)src_a / new_a; 01880 float dst_ratio = 1.0 - src_ratio; 01881 01882 new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON); 01883 new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON); 01884 new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON); 01885 01886 if (!layer_modes[layer.mode].affect_alpha) 01887 new_a = dst_a; 01888 01889 image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a)); 01890 } 01891 01892 01904 void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l, 01905 QImage& image, int m, int n) 01906 { 01907 int src = layer.image_tiles[j][i].pixelIndex(k, l); 01908 image.setPixel(m, n, src); 01909 } 01910 01911 01923 void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l, 01924 QImage& image, int m, int n) 01925 { 01926 int src = qGray(layer.image_tiles[j][i].pixel(k, l)); 01927 int dst = image.pixelIndex(m, n); 01928 01929 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 01930 01931 if (!src_a) return; // nothing to merge 01932 01933 switch (layer.mode) { 01934 case MULTIPLY_MODE: { 01935 src = INT_MULT( src, dst ); 01936 } 01937 break; 01938 case DIVIDE_MODE: { 01939 src = qMin((dst * 256) / (1 + src), 255); 01940 } 01941 break; 01942 case SCREEN_MODE: { 01943 src = 255 - INT_MULT(255 - dst, 255 - src); 01944 } 01945 break; 01946 case OVERLAY_MODE: { 01947 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst)); 01948 } 01949 break; 01950 case DIFFERENCE_MODE: { 01951 src = dst > src ? dst - src : src - dst; 01952 } 01953 break; 01954 case ADDITION_MODE: { 01955 src = add_lut(dst,src); 01956 } 01957 break; 01958 case SUBTRACT_MODE: { 01959 src = dst > src ? dst - src : 0; 01960 } 01961 break; 01962 case DARKEN_ONLY_MODE: { 01963 src = dst < src ? dst : src; 01964 } 01965 break; 01966 case LIGHTEN_ONLY_MODE: { 01967 src = dst < src ? src : dst; 01968 } 01969 break; 01970 case DODGE_MODE: { 01971 uint tmp = dst << 8; 01972 tmp /= 256 - src; 01973 src = (uchar) qMin(tmp, 255u); 01974 } 01975 break; 01976 case BURN_MODE: { 01977 uint tmp = (255-dst) << 8; 01978 tmp /= src + 1; 01979 src = (uchar) qMin(tmp, 255u); 01980 src = 255 - src; 01981 } 01982 break; 01983 case HARDLIGHT_MODE: { 01984 uint tmp; 01985 if (src > 128) { 01986 tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1)); 01987 src = (uchar) qMin(255 - (tmp >> 8), 255u); 01988 } else { 01989 tmp = (int) dst * ((int) src << 1); 01990 src = (uchar) qMin(tmp >> 8, 255u); 01991 } 01992 } 01993 break; 01994 case SOFTLIGHT_MODE: { 01995 uint tmpS, tmpM; 01996 01997 tmpM = INT_MULT(dst, src); 01998 tmpS = 255 - INT_MULT((255-dst), (255-src)); 01999 src = INT_MULT((255 - dst), tmpM) 02000 + INT_MULT(dst, tmpS); 02001 02002 } 02003 break; 02004 case GRAIN_EXTRACT_MODE: { 02005 int tmp; 02006 02007 tmp = dst - src + 128; 02008 tmp = qMin(tmp, 255); 02009 tmp = qMax(tmp, 0); 02010 02011 src = (uchar) tmp; 02012 } 02013 break; 02014 case GRAIN_MERGE_MODE: { 02015 int tmp; 02016 02017 tmp = dst + src - 128; 02018 tmp = qMin(tmp, 255); 02019 tmp = qMax(tmp, 0); 02020 02021 src = (uchar) tmp; 02022 } 02023 break; 02024 } 02025 02026 src_a = INT_MULT(src_a, layer.opacity); 02027 02028 // Apply the mask (if any) 02029 02030 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 02031 layer.mask_tiles[j].size() > (int)i) 02032 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 02033 02034 uchar new_a = OPAQUE_OPACITY; 02035 02036 float src_ratio = (float)src_a / new_a; 02037 float dst_ratio = 1.0 - src_ratio; 02038 02039 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON); 02040 02041 image.setPixel(m, n, new_g); 02042 } 02043 02044 02058 void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l, 02059 QImage& image, int m, int n) 02060 { 02061 QRgb src = layer.image_tiles[j][i].pixel(k, l); 02062 uchar src_a = layer.opacity; 02063 image.setPixel(m, n, qRgba(src, src_a)); 02064 } 02065 02066 02080 void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l, 02081 QImage& image, int m, int n) 02082 { 02083 int src = qGray(layer.image_tiles[j][i].pixel(k, l)); 02084 int dst = qGray(image.pixel(m, n)); 02085 02086 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 02087 uchar dst_a = qAlpha(image.pixel(m, n)); 02088 02089 if (!src_a) return; // nothing to merge 02090 02091 switch (layer.mode) { 02092 case MULTIPLY_MODE: { 02093 src = INT_MULT(src, dst); 02094 src_a = qMin(src_a, dst_a); 02095 } 02096 break; 02097 case DIVIDE_MODE: { 02098 src = qMin((dst * 256) / (1 + src), 255); 02099 src_a = qMin(src_a, dst_a); 02100 } 02101 break; 02102 case SCREEN_MODE: { 02103 src = 255 - INT_MULT(255 - dst, 255 - src); 02104 src_a = qMin(src_a, dst_a); 02105 } 02106 break; 02107 case OVERLAY_MODE: { 02108 src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst)); 02109 src_a = qMin(src_a, dst_a); 02110 } 02111 break; 02112 case DIFFERENCE_MODE: { 02113 src = dst > src ? dst - src : src - dst; 02114 src_a = qMin(src_a, dst_a); 02115 } 02116 break; 02117 case ADDITION_MODE: { 02118 src = add_lut(dst,src); 02119 src_a = qMin(src_a, dst_a); 02120 } 02121 break; 02122 case SUBTRACT_MODE: { 02123 src = dst > src ? dst - src : 0; 02124 src_a = qMin(src_a, dst_a); 02125 } 02126 break; 02127 case DARKEN_ONLY_MODE: { 02128 src = dst < src ? dst : src; 02129 src_a = qMin(src_a, dst_a); 02130 } 02131 break; 02132 case LIGHTEN_ONLY_MODE: { 02133 src = dst < src ? src : dst; 02134 src_a = qMin(src_a, dst_a); 02135 } 02136 break; 02137 case DODGE_MODE: { 02138 uint tmp = dst << 8; 02139 tmp /= 256 - src; 02140 src = (uchar) qMin(tmp, 255u); 02141 src_a = qMin(src_a, dst_a); 02142 } 02143 break; 02144 case BURN_MODE: { 02145 uint tmp = (255-dst) << 8; 02146 tmp /= src + 1; 02147 src = (uchar) qMin(tmp, 255u); 02148 src = 255 - src; 02149 src_a = qMin(src_a, dst_a); 02150 } 02151 break; 02152 case HARDLIGHT_MODE: { 02153 uint tmp; 02154 if (src > 128) { 02155 tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1)); 02156 src = (uchar) qMin(255 - (tmp >> 8), 255u); 02157 } else { 02158 tmp = (int) dst * ((int) src << 1); 02159 src = (uchar) qMin(tmp >> 8, 255u); 02160 } 02161 src_a = qMin(src_a, dst_a); 02162 } 02163 break; 02164 case SOFTLIGHT_MODE: { 02165 uint tmpS, tmpM; 02166 02167 tmpM = INT_MULT(dst, src); 02168 tmpS = 255 - INT_MULT((255 - dst), (255-src)); 02169 src = INT_MULT((255 - dst), tmpM) 02170 + INT_MULT(dst, tmpS); 02171 02172 src_a = qMin(src_a, dst_a); 02173 } 02174 break; 02175 case GRAIN_EXTRACT_MODE: { 02176 int tmp; 02177 02178 tmp = dst - src + 128; 02179 tmp = qMin(tmp, 255); 02180 tmp = qMax(tmp, 0); 02181 02182 src = (uchar) tmp; 02183 src_a = qMin(src_a, dst_a); 02184 } 02185 break; 02186 case GRAIN_MERGE_MODE: { 02187 int tmp; 02188 02189 tmp = dst + src - 128; 02190 tmp = qMin(tmp, 255); 02191 tmp = qMax(tmp, 0); 02192 02193 src = (uchar) tmp; 02194 src_a = qMin(src_a, dst_a); 02195 } 02196 break; 02197 } 02198 02199 src_a = INT_MULT(src_a, layer.opacity); 02200 02201 // Apply the mask (if any) 02202 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 02203 layer.mask_tiles[j].size() > (int)i) 02204 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 02205 02206 uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a); 02207 02208 float src_ratio = (float)src_a / new_a; 02209 float dst_ratio = 1.0 - src_ratio; 02210 02211 uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON); 02212 02213 if (!layer_modes[layer.mode].affect_alpha) 02214 new_a = dst_a; 02215 02216 image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a)); 02217 } 02218 02219 02231 void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l, 02232 QImage& image, int m, int n) 02233 { 02234 int src = layer.image_tiles[j][i].pixelIndex(k, l); 02235 image.setPixel(m, n, src); 02236 } 02237 02238 02250 void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l, 02251 QImage& image, int m, int n) 02252 { 02253 uchar src = layer.image_tiles[j][i].pixelIndex(k, l); 02254 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 02255 src_a = INT_MULT( src_a, layer.opacity ); 02256 02257 if ( layer.apply_mask == 1 && 02258 layer.mask_tiles.size() > (int)j && 02259 layer.mask_tiles[j].size() > (int)i) 02260 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 02261 02262 if (src_a > 127) { 02263 src++; 02264 image.setPixel(m, n, src); 02265 } 02266 } 02267 02268 02282 void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l, 02283 QImage& image, int m, int n) 02284 { 02285 QRgb src = layer.image_tiles[j][i].pixel(k, l); 02286 uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l); 02287 src_a = INT_MULT(src_a, layer.opacity); 02288 02289 // Apply the mask (if any) 02290 if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j && 02291 layer.mask_tiles[j].size() > (int)i) 02292 src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l)); 02293 02294 // This is what appears in the GIMP window 02295 if (src_a <= 127) 02296 src_a = 0; 02297 else 02298 src_a = OPAQUE_OPACITY; 02299 02300 image.setPixel(m, n, qRgba(src, src_a)); 02301 } 02302 02303 02311 void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y ) 02312 { 02313 // The apparently spurious rand() calls are to wind the random 02314 // numbers up to the same point for each tile. 02315 02316 for (int l = 0; l < image.height(); l++) { 02317 srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]); 02318 02319 for (int k = 0; k < x; k++) 02320 rand(); 02321 02322 for (int k = 0; k < image.width(); k++) { 02323 int rand_val = rand() & 0xff; 02324 QRgb pixel = image.pixel(k, l); 02325 02326 if (rand_val > qAlpha(pixel)) { 02327 image.setPixel(k, l, qRgba(pixel, 0)); 02328 } 02329 } 02330 } 02331 } 02332 02333 02343 void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y ) 02344 { 02345 // The apparently spurious rand() calls are to wind the random 02346 // numbers up to the same point for each tile. 02347 02348 for (int l = 0; l < image.height(); l++) { 02349 srand( random_table[(l + y) % RANDOM_TABLE_SIZE]); 02350 02351 for (int k = 0; k < x; k++) 02352 rand(); 02353 02354 for (int k = 0; k < image.width(); k++) { 02355 int rand_val = rand() & 0xff; 02356 uchar alpha = image.pixelIndex(k, l); 02357 02358 if (rand_val > alpha) { 02359 image.setPixel(k, l, 0); 02360 } 02361 } 02362 } 02363 } 02364 02365 02367 02368 XCFHandler::XCFHandler() 02369 { 02370 } 02371 02372 bool XCFHandler::canRead() const 02373 { 02374 if (canRead(device())) { 02375 setFormat("xcf"); 02376 return true; 02377 } 02378 return false; 02379 } 02380 02381 bool XCFHandler::read(QImage *image) 02382 { 02383 XCFImageFormat xcfif; 02384 return xcfif.readXCF(device(), image); 02385 } 02386 02387 bool XCFHandler::write(const QImage &) 02388 { 02389 return false; 02390 } 02391 02392 QByteArray XCFHandler::name() const 02393 { 02394 return "xcf"; 02395 } 02396 02397 bool XCFHandler::canRead(QIODevice *device) 02398 { 02399 if (!device) { 02400 qWarning("DDSHandler::canRead() called with no device"); 02401 return false; 02402 } 02403 02404 qint64 oldPos = device->pos(); 02405 02406 char head[8]; 02407 qint64 readBytes = device->read(head, sizeof(head)); 02408 if (readBytes != sizeof(head)) { 02409 if (device->isSequential()) { 02410 while (readBytes > 0) 02411 device->ungetChar(head[readBytes-- - 1]); 02412 } else { 02413 device->seek(oldPos); 02414 } 02415 return false; 02416 } 02417 02418 if (device->isSequential()) { 02419 while (readBytes > 0) 02420 device->ungetChar(head[readBytes-- - 1]); 02421 } else { 02422 device->seek(oldPos); 02423 } 02424 02425 return qstrncmp(head, "gimp xcf", 8) == 0; 02426 } 02427 02428 02429 class XCFPlugin : public QImageIOPlugin 02430 { 02431 public: 02432 QStringList keys() const; 02433 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 02434 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 02435 }; 02436 02437 QStringList XCFPlugin::keys() const 02438 { 02439 return QStringList() << "xcf" << "XCF"; 02440 } 02441 02442 QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const 02443 { 02444 if (format == "xcf" || format == "XCF") 02445 return Capabilities(CanRead); 02446 if (!format.isEmpty()) 02447 return 0; 02448 if (!device->isOpen()) 02449 return 0; 02450 02451 Capabilities cap; 02452 if (device->isReadable() && XCFHandler::canRead(device)) 02453 cap |= CanRead; 02454 return cap; 02455 } 02456 02457 QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const 02458 { 02459 QImageIOHandler *handler = new XCFHandler; 02460 handler->setDevice(device); 02461 handler->setFormat(format); 02462 return handler; 02463 } 02464 02465 Q_EXPORT_STATIC_PLUGIN(XCFPlugin) 02466 Q_EXPORT_PLUGIN2(xcf,XCFPlugin)
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:50:56 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:50:56 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.