ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

tga.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 /* Based on code from xoreos https://github.com/DrMcCoy/xoreos/
00024  * relicensed under GPLv2+ with permission from DrMcCoy and clone2727
00025  */
00026 
00027 #include "image/tga.h"
00028 
00029 #include "common/util.h"
00030 #include "common/algorithm.h"
00031 #include "common/stream.h"
00032 #include "common/textconsole.h"
00033 #include "common/error.h"
00034 
00035 namespace Image {
00036 
00037 TGADecoder::TGADecoder() {
00038     _colorMapSize = 0;
00039     _colorMapOrigin = 0;
00040     _colorMapLength = 0;
00041     _colorMapEntryLength = 0;
00042     _colorMap = NULL;
00043 }
00044 
00045 TGADecoder::~TGADecoder() {
00046     destroy();
00047 }
00048 
00049 void TGADecoder::destroy() {
00050     _surface.free();
00051     delete[] _colorMap;
00052 }
00053 
00054 bool TGADecoder::loadStream(Common::SeekableReadStream &tga) {
00055     destroy();
00056 
00057     byte imageType, pixelDepth;
00058     bool success;
00059     success = readHeader(tga, imageType, pixelDepth);
00060     if (success) {
00061         switch (imageType) {
00062         case TYPE_BW:
00063         case TYPE_TRUECOLOR:
00064             success = readData(tga, imageType, pixelDepth);
00065             break;
00066         case TYPE_RLE_BW:
00067         case TYPE_RLE_TRUECOLOR:
00068         case TYPE_RLE_CMAP:
00069             success = readDataRLE(tga, imageType, pixelDepth);
00070             break;
00071         case TYPE_CMAP:
00072             success = readDataColorMapped(tga, imageType, pixelDepth);
00073             break;
00074         default:
00075             success = false;
00076             break;
00077         }
00078     }
00079     if (tga.err() || !success) {
00080         warning("Failed reading TGA-file");
00081         return false;
00082     }
00083     return success;
00084 }
00085 
00086 bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) {
00087     if (!tga.seek(0)) {
00088         warning("Failed reading TGA-file");
00089         return false;
00090     }
00091 
00092     // TGAs have an optional "id" string in the header
00093     uint32 idLength = tga.readByte();
00094 
00095     // Number of colors in the color map / palette
00096     int hasColorMap = tga.readByte();
00097 
00098     // Image type. See header for numeric constants
00099     imageType = tga.readByte();
00100 
00101     switch (imageType) {
00102     case TYPE_CMAP:
00103     case TYPE_TRUECOLOR:
00104     case TYPE_BW:
00105     case TYPE_RLE_CMAP:
00106     case TYPE_RLE_TRUECOLOR:
00107     case TYPE_RLE_BW:
00108         break;
00109     default:
00110         warning("Unsupported image type: %d", imageType);
00111         return false;
00112     }
00113 
00114     // Color map specifications
00115     if (hasColorMap == 0) {
00116         tga.skip(5);
00117     } else {
00118         _colorMapOrigin = tga.readUint16LE();
00119         _colorMapLength = tga.readUint16LE();
00120         _colorMapEntryLength = tga.readByte();
00121     }
00122     // Origin-defintions
00123     tga.skip(2 + 2);
00124 
00125     // Image dimensions
00126     _surface.w = tga.readUint16LE();
00127     _surface.h = tga.readUint16LE();
00128 
00129     // Bits per pixel
00130     pixelDepth = tga.readByte();
00131     _surface.format.bytesPerPixel = pixelDepth / 8;
00132 
00133     // Image descriptor
00134     byte imgDesc = tga.readByte();
00135     int attributeBits = imgDesc & 0x0F;
00136     assert((imgDesc & 0x10) == 0);
00137     _originTop = (imgDesc & 0x20);
00138 
00139     // Interleaving is not handled at this point
00140     //int interleave = (imgDesc & 0xC);
00141     if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) {
00142         if (pixelDepth == 8) {
00143             _format = Graphics::PixelFormat::createFormatCLUT8();
00144         } else {
00145             warning("Unsupported index-depth: %d", pixelDepth);
00146             return false;
00147         }
00148     } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) {
00149         if (pixelDepth == 24) {
00150             _format = Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
00151         } else if (pixelDepth == 32) {
00152             // HACK: According to the spec, attributeBits should determine the amount
00153             // of alpha-bits, however, as the game files that use this decoder seems
00154             // to ignore that fact, we force the amount to 8 for 32bpp files for now.
00155             _format = Graphics::PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24);
00156         } else if (pixelDepth == 16) {
00157             // 16bpp TGA is ARGB1555
00158             _format = Graphics::PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15);
00159         } else {
00160             warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
00161             return false;
00162         }
00163     } else if (imageType == TYPE_BW || imageType == TYPE_RLE_BW) {
00164         if (pixelDepth == 8) {
00165             _format = Graphics::PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0);
00166         } else {
00167             warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
00168             return false;
00169         }
00170 
00171     } else {
00172         warning("Unsupported image type: %d", imageType);
00173         return false;
00174     }
00175 
00176     // Skip the id string
00177     tga.skip(idLength);
00178 
00179     if (hasColorMap) {
00180         return readColorMap(tga, imageType, pixelDepth);
00181     }
00182     return true;
00183 }
00184 
00185 bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
00186     _colorMap = new byte[3 * _colorMapLength];
00187     for (int i = 0; i < _colorMapLength * 3; i += 3) {
00188         byte r, g, b;
00189         if (_colorMapEntryLength == 32) {
00190             byte a;
00191             Graphics::PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24);
00192             uint32 color = tga.readUint32LE();
00193             format.colorToARGB(color, a, r, g, b);
00194         } else if (_colorMapEntryLength == 24) {
00195             r = tga.readByte();
00196             g = tga.readByte();
00197             b = tga.readByte();
00198         } else if (_colorMapEntryLength == 16) {
00199             byte a;
00200             Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15);
00201             uint16 color = tga.readUint16LE();
00202             format.colorToARGB(color, a, r, g, b);
00203         } else {
00204             warning("Unsupported image type: %d", imageType);
00205             r = g = b = 0;
00206         }
00207 #ifdef SCUMM_LITTLE_ENDIAN
00208         _colorMap[i] = r;
00209         _colorMap[i + 1] = g;
00210         _colorMap[i + 2] = b;
00211 #else
00212         _colorMap[i] = b;
00213         _colorMap[i + 1] = g;
00214         _colorMap[i + 2] = r;
00215 #endif
00216     }
00217     return true;
00218 }
00219 
00220 // Additional information found from http://paulbourke.net/dataformats/tga/
00221 // With some details from the link referenced in the header.
00222 bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
00223     // TrueColor
00224     if (imageType == TYPE_TRUECOLOR) {
00225         _surface.create(_surface.w, _surface.h, _format);
00226 
00227         if (pixelDepth == 16) {
00228             for (int i = 0; i < _surface.h; i++) {
00229                 uint16 *dst;
00230                 if (!_originTop) {
00231                     dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1);
00232                 } else {
00233                     dst = (uint16 *)_surface.getBasePtr(0, i);
00234                 }
00235                 for (int j = 0; j < _surface.w; j++) {
00236                     *dst++ = tga.readUint16LE();
00237                 }
00238             }
00239         } else if (pixelDepth == 32) {
00240             for (int i = 0; i < _surface.h; i++) {
00241                 uint32 *dst;
00242                 if (!_originTop) {
00243                     dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1);
00244                 } else {
00245                     dst = (uint32 *)_surface.getBasePtr(0, i);
00246                 }
00247                 for (int j = 0; j < _surface.w; j++) {
00248                     *dst++ = tga.readUint32LE();
00249                 }
00250             }
00251         } else if (pixelDepth == 24) {
00252             for (int i = 0; i < _surface.h; i++) {
00253                 byte *dst;
00254                 if (!_originTop) {
00255                     dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
00256                 } else {
00257                     dst = (byte *)_surface.getBasePtr(0, i);
00258                 }
00259                 for (int j = 0; j < _surface.w; j++) {
00260                     byte r = tga.readByte();
00261                     byte g = tga.readByte();
00262                     byte b = tga.readByte();
00263 #ifdef SCUMM_LITTLE_ENDIAN
00264                     *dst++ = r;
00265                     *dst++ = g;
00266                     *dst++ = b;
00267 #else
00268                     *dst++ = b;
00269                     *dst++ = g;
00270                     *dst++ = r;
00271 #endif
00272                 }
00273             }
00274         }
00275         // Black/White
00276     } else if (imageType == TYPE_BW) {
00277         _surface.create(_surface.w, _surface.h, _format);
00278 
00279         byte *data  = (byte *)_surface.getPixels();
00280         uint32 count = _surface.w * _surface.h;
00281 
00282         while (count-- > 0) {
00283             byte g = tga.readByte();
00284             *data++ = g;
00285             *data++ = g;
00286             *data++ = g;
00287             *data++ = g;
00288         }
00289     }
00290     return true;
00291 }
00292 
00293 bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) {
00294     // Color-mapped
00295     if (imageType == TYPE_CMAP) {
00296         _surface.create(_surface.w, _surface.h, _format);
00297         if (indexDepth == 8) {
00298             for (int i = 0; i < _surface.h; i++) {
00299                 byte *dst;
00300                 if (!_originTop) {
00301                     dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
00302                 } else {
00303                     dst = (byte *)_surface.getBasePtr(0, i);
00304                 }
00305                 for (int j = 0; j < _surface.w; j++) {
00306                     byte index = tga.readByte();
00307                     *dst++ = index;
00308                 }
00309             }
00310         } else if (indexDepth == 16) {
00311             warning("16 bit indexes not supported");
00312             return false;
00313         }
00314     } else {
00315         return false;
00316     }
00317     return true;
00318 }
00319 
00320 bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
00321     // RLE-TrueColor / RLE-Black/White
00322     if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) {
00323         _surface.create(_surface.w, _surface.h, _format);
00324         uint32 count = _surface.w * _surface.h;
00325         byte *data = (byte *)_surface.getPixels();
00326 
00327         while (count > 0) {
00328             uint32 header = tga.readByte();
00329             byte type = (header & 0x80) >> 7;
00330             uint32 rleCount = (header & 0x7F) + 1;
00331 
00332             // RLE-packet
00333             if (type == 1) {
00334                 if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
00335                     uint32 color = tga.readUint32LE();
00336                     while (rleCount-- > 0) {
00337                         *((uint32 *)data) = color;
00338                         data += 4;
00339                         count--;
00340                     }
00341                 } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
00342                     byte r = tga.readByte();
00343                     byte g = tga.readByte();
00344                     byte b = tga.readByte();
00345                     while (rleCount-- > 0) {
00346 #ifdef SCUMM_LITTLE_ENDIAN
00347                         *data++ = r;
00348                         *data++ = g;
00349                         *data++ = b;
00350 #else
00351                         *data++ = b;
00352                         *data++ = g;
00353                         *data++ = r;
00354 #endif
00355                         count--;
00356                     }
00357                 } else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) {
00358                     const uint16 rgb = tga.readUint16LE();
00359                     while (rleCount-- > 0) {
00360                         *((uint16 *)data) = rgb;
00361                         data += 2;
00362                         count--;
00363                     }
00364                 } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
00365                     byte color = tga.readByte();
00366                     while (rleCount-- > 0) {
00367                         *data++ = color;
00368                         *data++ = color;
00369                         *data++ = color;
00370                         *data++ = color;
00371                         count--;
00372                     }
00373                 } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
00374                     byte index = tga.readByte();
00375                     while (rleCount-- > 0) {
00376                         *data++ = index;
00377                         count--;
00378                     }
00379                 } else {
00380                     warning("Unhandled pixel-depth for image-type 10");
00381                     return false;
00382                 }
00383                 // Raw-packet
00384             } else if (type == 0) {
00385                 if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
00386                     while (rleCount-- > 0) {
00387                         uint32 color = tga.readUint32LE();
00388                         *((uint32 *)data) = color;
00389                         data += 4;
00390                         count--;
00391                     }
00392                 } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
00393                     while (rleCount-- > 0) {
00394                         byte r = tga.readByte();
00395                         byte g = tga.readByte();
00396                         byte b = tga.readByte();
00397 #ifdef SCUMM_LITTLE_ENDIAN
00398                         *data++ = r;
00399                         *data++ = g;
00400                         *data++ = b;
00401 #else
00402                         *data++ = b;
00403                         *data++ = g;
00404                         *data++ = r;
00405 #endif
00406                         count--;
00407                     }
00408                 } else if (pixelDepth == 16 && imageType == TYPE_RLE_TRUECOLOR) {
00409                     while (rleCount-- > 0) {
00410                         *((uint16 *)data) = tga.readUint16LE();
00411                         data += 2;
00412                         count--;
00413                     }
00414                 } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
00415                     while (rleCount-- > 0) {
00416                         byte color = tga.readByte();
00417                         *data++ = color;
00418                         *data++ = color;
00419                         *data++ = color;
00420                         *data++ = color;
00421                         count--;
00422                     }
00423                 } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
00424                     while (rleCount-- > 0) {
00425                         byte index = tga.readByte();
00426                         *data++ = index;
00427                         count--;
00428                     }
00429                 } else {
00430                     warning("Unhandled pixel-depth for image-type 10");
00431                     return false;
00432                 }
00433             } else {
00434                 warning("Unknown header for RLE-packet %d", type);
00435                 return false;
00436             }
00437         }
00438     } else {
00439         return false;
00440     }
00441 
00442     // If it's a bottom origin image, we need to vertically flip the image
00443     if (!_originTop) {
00444         byte *tempLine = new byte[_surface.pitch];
00445         byte *line1 = (byte *)_surface.getBasePtr(0, 0);
00446         byte *line2 = (byte *)_surface.getBasePtr(0, _surface.h - 1);
00447 
00448         for (int y = 0; y < (_surface.h / 2); ++y, line1 += _surface.pitch, line2 -= _surface.pitch) {
00449             Common::copy(line1, line1 + _surface.pitch, tempLine);
00450             Common::copy(line2, line2 + _surface.pitch, line1);
00451             Common::copy(tempLine, tempLine + _surface.pitch, line2);
00452         }
00453 
00454         delete[] tempLine;
00455     }
00456 
00457     return true;
00458 }
00459 
00460 } // End of namespace Image


Generated on Sat Mar 23 2019 05:02:16 for ResidualVM by doxygen 1.7.1
curved edge   curved edge