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

rpza.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 off ffmpeg's RPZA decoder
00024 
00025 #include "image/codecs/rpza.h"
00026 
00027 #include "common/debug.h"
00028 #include "common/system.h"
00029 #include "common/stream.h"
00030 #include "common/textconsole.h"
00031 
00032 namespace Image {
00033 
00034 RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() {
00035     _format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
00036     _ditherPalette = 0;
00037     _dirtyPalette = false;
00038     _colorMap = 0;
00039     _width = width;
00040     _height = height;
00041     _blockWidth = (width + 3) / 4;
00042     _blockHeight = (height + 3) / 4;
00043     _surface = 0;
00044 }
00045 
00046 RPZADecoder::~RPZADecoder() {
00047     if (_surface) {
00048         _surface->free();
00049         delete _surface;
00050     }
00051 
00052     delete[] _ditherPalette;
00053     delete[] _colorMap;
00054 }
00055 
00056 #define ADVANCE_BLOCK() \
00057     blockPtr += 4; \
00058     if (blockPtr >= endPtr) { \
00059         blockPtr += pitch * 3; \
00060         endPtr = blockPtr + pitch; \
00061     } \
00062     totalBlocks--; \
00063     if (totalBlocks < 0) \
00064         error("rpza block counter just went negative (this should not happen)") \
00065 
00066 struct BlockDecoderRaw {
00067     static inline void drawFillBlock(uint16 *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
00068         blockPtr[0] = color;
00069         blockPtr[1] = color;
00070         blockPtr[2] = color;
00071         blockPtr[3] = color;
00072         blockPtr += pitch;
00073         blockPtr[0] = color;
00074         blockPtr[1] = color;
00075         blockPtr[2] = color;
00076         blockPtr[3] = color;
00077         blockPtr += pitch;
00078         blockPtr[0] = color;
00079         blockPtr[1] = color;
00080         blockPtr[2] = color;
00081         blockPtr[3] = color;
00082         blockPtr += pitch;
00083         blockPtr[0] = color;
00084         blockPtr[1] = color;
00085         blockPtr[2] = color;
00086         blockPtr[3] = color;
00087     }
00088 
00089     static inline void drawRawBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
00090         blockPtr[0] = colors[0];
00091         blockPtr[1] = colors[1];
00092         blockPtr[2] = colors[2];
00093         blockPtr[3] = colors[3];
00094         blockPtr += pitch;
00095         blockPtr[0] = colors[4];
00096         blockPtr[1] = colors[5];
00097         blockPtr[2] = colors[6];
00098         blockPtr[3] = colors[7];
00099         blockPtr += pitch;
00100         blockPtr[0] = colors[8];
00101         blockPtr[1] = colors[9];
00102         blockPtr[2] = colors[10];
00103         blockPtr[3] = colors[11];
00104         blockPtr += pitch;
00105         blockPtr[0] = colors[12];
00106         blockPtr[1] = colors[13];
00107         blockPtr[2] = colors[14];
00108         blockPtr[3] = colors[15];
00109     }
00110 
00111     static inline void drawBlendBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
00112         blockPtr[0] = colors[(indexes[0] >> 6) & 0x03];
00113         blockPtr[1] = colors[(indexes[0] >> 4) & 0x03];
00114         blockPtr[2] = colors[(indexes[0] >> 2) & 0x03];
00115         blockPtr[3] = colors[(indexes[0] >> 0) & 0x03];
00116         blockPtr += pitch;
00117         blockPtr[0] = colors[(indexes[1] >> 6) & 0x03];
00118         blockPtr[1] = colors[(indexes[1] >> 4) & 0x03];
00119         blockPtr[2] = colors[(indexes[1] >> 2) & 0x03];
00120         blockPtr[3] = colors[(indexes[1] >> 0) & 0x03];
00121         blockPtr += pitch;
00122         blockPtr[0] = colors[(indexes[2] >> 6) & 0x03];
00123         blockPtr[1] = colors[(indexes[2] >> 4) & 0x03];
00124         blockPtr[2] = colors[(indexes[2] >> 2) & 0x03];
00125         blockPtr[3] = colors[(indexes[2] >> 0) & 0x03];
00126         blockPtr += pitch;
00127         blockPtr[0] = colors[(indexes[3] >> 6) & 0x03];
00128         blockPtr[1] = colors[(indexes[3] >> 4) & 0x03];
00129         blockPtr[2] = colors[(indexes[3] >> 2) & 0x03];
00130         blockPtr[3] = colors[(indexes[3] >> 0) & 0x03];
00131     }
00132 };
00133 
00134 struct BlockDecoderDither {
00135     static inline void drawFillBlock(byte *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
00136         const byte *mapOffset = colorMap + (color >> 1);
00137         byte pixel1 = mapOffset[0x0000];
00138         byte pixel2 = mapOffset[0x4000];
00139         byte pixel3 = mapOffset[0x8000];
00140         byte pixel4 = mapOffset[0xC000];
00141 
00142         blockPtr[0] = pixel1;
00143         blockPtr[1] = pixel2;
00144         blockPtr[2] = pixel3;
00145         blockPtr[3] = pixel4;
00146         blockPtr += pitch;
00147         blockPtr[0] = pixel4;
00148         blockPtr[1] = pixel1;
00149         blockPtr[2] = pixel2;
00150         blockPtr[3] = pixel3;
00151         blockPtr += pitch;
00152         blockPtr[0] = pixel2;
00153         blockPtr[1] = pixel3;
00154         blockPtr[2] = pixel4;
00155         blockPtr[3] = pixel1;
00156         blockPtr += pitch;
00157         blockPtr[0] = pixel3;
00158         blockPtr[1] = pixel4;
00159         blockPtr[2] = pixel1;
00160         blockPtr[3] = pixel2;
00161     }
00162 
00163     static inline void drawRawBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
00164         blockPtr[0] = colorMap[(colors[0] >> 1) + 0x0000];
00165         blockPtr[1] = colorMap[(colors[1] >> 1) + 0x4000];
00166         blockPtr[2] = colorMap[(colors[2] >> 1) + 0x8000];
00167         blockPtr[3] = colorMap[(colors[3] >> 1) + 0xC000];
00168         blockPtr += pitch;
00169         blockPtr[0] = colorMap[(colors[4] >> 1) + 0xC000];
00170         blockPtr[1] = colorMap[(colors[5] >> 1) + 0x0000];
00171         blockPtr[2] = colorMap[(colors[6] >> 1) + 0x4000];
00172         blockPtr[3] = colorMap[(colors[7] >> 1) + 0x8000];
00173         blockPtr += pitch;
00174         blockPtr[0] = colorMap[(colors[8] >> 1) + 0x4000];
00175         blockPtr[1] = colorMap[(colors[9] >> 1) + 0x8000];
00176         blockPtr[2] = colorMap[(colors[10] >> 1) + 0xC000];
00177         blockPtr[3] = colorMap[(colors[11] >> 1) + 0x0000];
00178         blockPtr += pitch;
00179         blockPtr[0] = colorMap[(colors[12] >> 1) + 0x8000];
00180         blockPtr[1] = colorMap[(colors[13] >> 1) + 0xC000];
00181         blockPtr[2] = colorMap[(colors[14] >> 1) + 0x0000];
00182         blockPtr[3] = colorMap[(colors[15] >> 1) + 0x4000];
00183     }
00184 
00185     static inline void drawBlendBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
00186         blockPtr[0] = colorMap[(colors[(indexes[0] >> 6) & 0x03] >> 1) + 0x0000];
00187         blockPtr[1] = colorMap[(colors[(indexes[0] >> 4) & 0x03] >> 1) + 0x4000];
00188         blockPtr[2] = colorMap[(colors[(indexes[0] >> 2) & 0x03] >> 1) + 0x8000];
00189         blockPtr[3] = colorMap[(colors[(indexes[0] >> 0) & 0x03] >> 1) + 0xC000];
00190         blockPtr += pitch;
00191         blockPtr[0] = colorMap[(colors[(indexes[1] >> 6) & 0x03] >> 1) + 0xC000];
00192         blockPtr[1] = colorMap[(colors[(indexes[1] >> 4) & 0x03] >> 1) + 0x0000];
00193         blockPtr[2] = colorMap[(colors[(indexes[1] >> 2) & 0x03] >> 1) + 0x4000];
00194         blockPtr[3] = colorMap[(colors[(indexes[1] >> 0) & 0x03] >> 1) + 0x8000];
00195         blockPtr += pitch;
00196         blockPtr[0] = colorMap[(colors[(indexes[2] >> 6) & 0x03] >> 1) + 0x4000];
00197         blockPtr[1] = colorMap[(colors[(indexes[2] >> 4) & 0x03] >> 1) + 0x8000];
00198         blockPtr[2] = colorMap[(colors[(indexes[2] >> 2) & 0x03] >> 1) + 0xC000];
00199         blockPtr[3] = colorMap[(colors[(indexes[2] >> 0) & 0x03] >> 1) + 0x0000];
00200         blockPtr += pitch;
00201         blockPtr[0] = colorMap[(colors[(indexes[3] >> 6) & 0x03] >> 1) + 0x8000];
00202         blockPtr[1] = colorMap[(colors[(indexes[3] >> 4) & 0x03] >> 1) + 0xC000];
00203         blockPtr[2] = colorMap[(colors[(indexes[3] >> 2) & 0x03] >> 1) + 0x0000];
00204         blockPtr[3] = colorMap[(colors[(indexes[3] >> 0) & 0x03] >> 1) + 0x4000];
00205     }
00206 };
00207 
00208 template<typename PixelInt, typename BlockDecoder>
00209 static inline void decodeFrameTmpl(Common::SeekableReadStream &stream, PixelInt *ptr, uint16 pitch, uint16 blockWidth, uint16 blockHeight, const byte *colorMap) {
00210     uint16 colorA = 0, colorB = 0;
00211     uint16 color4[4];
00212 
00213     PixelInt *blockPtr = ptr;
00214     PixelInt *endPtr = ptr + pitch;
00215     uint16 ta;
00216     uint16 tb;
00217 
00218     // First byte is always 0xe1. Warn if it's different
00219     byte firstByte = stream.readByte();
00220     if (firstByte != 0xe1)
00221         warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte);
00222 
00223     // Get chunk size, ingnoring first byte
00224     uint32 chunkSize = stream.readUint16BE() << 8;
00225     chunkSize += stream.readByte();
00226 
00227     // If length mismatch use size from MOV file and try to decode anyway
00228     if (chunkSize != (uint32)stream.size()) {
00229         warning("MOV chunk size != encoded chunk size; using MOV chunk size");
00230         chunkSize = stream.size();
00231     }
00232 
00233     // Number of 4x4 blocks in frame
00234     int32 totalBlocks = blockWidth * blockHeight;
00235 
00236     // Process chunk data
00237     while ((uint32)stream.pos() < chunkSize) {
00238         byte opcode = stream.readByte(); // Get opcode
00239         byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode
00240 
00241         // If opcode MSbit is 0, we need more data to decide what to do
00242         if ((opcode & 0x80) == 0) {
00243             colorA = (opcode << 8) | stream.readByte();
00244             opcode = 0;
00245             if (stream.readByte() & 0x80) {
00246                 // Must behave as opcode 110xxxxx, using colorA computed
00247                 // above. Use fake opcode 0x20 to enter switch block at
00248                 // the right place
00249                 opcode = 0x20;
00250                 numBlocks = 1;
00251             }
00252             stream.seek(-1, SEEK_CUR);
00253         }
00254 
00255         switch (opcode & 0xe0) {
00256         case 0x80: // Skip blocks
00257             while (numBlocks--) {
00258                 ADVANCE_BLOCK();
00259             }
00260             break;
00261         case 0xa0: // Fill blocks with one color
00262             colorA = stream.readUint16BE();
00263 
00264             while (numBlocks--) {
00265                 BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
00266                 ADVANCE_BLOCK();
00267             }
00268             break;
00269 
00270         // Fill blocks with 4 colors
00271         case 0xc0:
00272             colorA = stream.readUint16BE();
00273             // fall through
00274         case 0x20:
00275             colorB = stream.readUint16BE();
00276 
00277             // Sort out the colors
00278             color4[0] = colorB & 0x7FFF;
00279             color4[1] = 0;
00280             color4[2] = 0;
00281             color4[3] = colorA & 0x7FFF;
00282 
00283             // Red components
00284             ta = (colorA >> 10) & 0x1F;
00285             tb = (colorB >> 10) & 0x1F;
00286             color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
00287             color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
00288 
00289             // Green components
00290             ta = (colorA >> 5) & 0x1F;
00291             tb = (colorB >> 5) & 0x1F;
00292             color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
00293             color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
00294 
00295             // Blue components
00296             ta = colorA & 0x1F;
00297             tb = colorB & 0x1F;
00298             color4[1] |= ((11 * ta + 21 * tb) >> 5);
00299             color4[2] |= ((21 * ta + 11 * tb) >> 5);
00300 
00301             while (numBlocks--) {
00302                 byte indexes[4];
00303                 stream.read(indexes, 4);
00304 
00305                 BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
00306                 ADVANCE_BLOCK();
00307             }
00308             break;
00309 
00310         // Fill block with 16 colors
00311         case 0x00: {
00312             uint16 colors[16];
00313             colors[0] = colorA;
00314 
00315             for (int i = 0; i < 15; i++)
00316                 colors[i + 1] = stream.readUint16BE();
00317 
00318             BlockDecoder::drawRawBlock(blockPtr, pitch, colors, colorMap);
00319             ADVANCE_BLOCK();
00320             break;
00321         }
00322 
00323         // Unknown opcode
00324         default:
00325             error("Unknown opcode %02x in rpza chunk", opcode);
00326         }
00327     }
00328 }
00329 
00330 const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
00331     if (!_surface) {
00332         _surface = new Graphics::Surface();
00333 
00334         // Allocate enough space in the surface for the blocks
00335         _surface->create(_blockWidth * 4, _blockHeight * 4, getPixelFormat());
00336 
00337         // Adjust width/height to be the right ones
00338         _surface->w = _width;
00339         _surface->h = _height;
00340     }
00341 
00342     if (_colorMap)
00343         decodeFrameTmpl<byte, BlockDecoderDither>(stream, (byte *)_surface->getPixels(), _surface->pitch, _blockWidth, _blockHeight, _colorMap);
00344     else
00345         decodeFrameTmpl<uint16, BlockDecoderRaw>(stream, (uint16 *)_surface->getPixels(), _surface->pitch / 2, _blockWidth, _blockHeight, _colorMap);
00346 
00347     return _surface;
00348 }
00349 
00350 bool RPZADecoder::canDither(DitherType type) const {
00351     return type == kDitherTypeQT;
00352 }
00353 
00354 void RPZADecoder::setDither(DitherType type, const byte *palette) {
00355     assert(canDither(type));
00356 
00357     _ditherPalette = new byte[256 * 3];
00358     memcpy(_ditherPalette, palette, 256 * 3);
00359 
00360     _dirtyPalette = true;
00361     _format = Graphics::PixelFormat::createFormatCLUT8();
00362 
00363     delete[] _colorMap;
00364     _colorMap = createQuickTimeDitherTable(palette, 256);
00365 }
00366 
00367 } // End of namespace Image


Generated on Sat Mar 16 2019 05:01:52 for ResidualVM by doxygen 1.7.1
curved edge   curved edge