X Tutup
/* ScummVM Tools * * ScummVM Tools is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include "encode_dxa.h" #include "common/endian.h" const uint32 typeDEXA = 0x41584544; const uint32 typeFRAM = 0x4d415246; //const uint32 typeWAVE = 0x45564157; const uint32 typeCMAP = 0x50414D43; const uint32 typeNULL = 0x4C4C554E; #define BUFFER_LEN 1024 // other block dimensions than 4x4 are not really supported yet #define BLOCKW 4 #define BLOCKH 4 struct DiffStruct { uint16 map; int count; byte pixels[BLOCKW*BLOCKH]; }; class DxaEncoder { private: Common::File _dxa; int _width, _height, _framerate, _framecount, _workheight; uint8 *_prevframe, *_prevpalette; ScaleMode _scaleMode; byte *_codeBuf, *_dataBuf, *_motBuf, *_maskBuf; void grabBlock(byte *frame, int x, int y, int blockw, int blockh, byte *block); bool m13blocksAreEqual(byte *frame, int x, int y, int x2, int y2, int w, int h); bool m13blockIsSolidColor(byte *frame, int x, int y, int w, int h, byte &color); void m13blockDelta(byte *frame, int x, int y, int x2, int y2, DiffStruct &diff); bool m13motionVector(byte *frame, int x, int y, int w, int h, int &mx, int &my); int m13countColors(byte *block, byte *pixels, unsigned long &code, int &codeSize); uLong m13encode(byte *frame, byte *outbuf); public: DxaEncoder(Tool &tool, Common::Filename filename, int width, int height, int framerate, ScaleMode scaleMode); ~DxaEncoder(); void writeHeader(); void writeNULL(); void writeFrame(uint8 *frame, uint8 *palette); }; DxaEncoder::DxaEncoder(Tool &tool, Common::Filename filename, int width, int height, int framerate, ScaleMode scaleMode) { _dxa.open(filename, "wb"); _width = width; _height = height; _framerate = framerate; _framecount = 0; _prevframe = new uint8[_width * _height]; _prevpalette = new uint8[768]; _scaleMode = scaleMode; _workheight = _scaleMode == S_NONE ? _height : _height / 2; _codeBuf = new byte[_width * _height / 16]; _dataBuf = new byte[_width * _height]; _motBuf = new byte[_width * _height]; _maskBuf = new byte[_width * _height]; writeHeader(); } DxaEncoder::~DxaEncoder() { _dxa.seek(0, SEEK_SET); writeHeader(); delete[] _codeBuf; delete[] _dataBuf; delete[] _motBuf; delete[] _maskBuf; delete[] _prevframe; delete[] _prevpalette; } void DxaEncoder::writeHeader() { //DEXA uint8 version = 0; /* remember the scaling mode */ if (_scaleMode == S_INTERLACED) version |= 0x80; else if (_scaleMode == S_DOUBLE) version |= 0x40; _dxa.writeUint32LE(typeDEXA); _dxa.writeByte(version); _dxa.writeUint16BE(_framecount); _dxa.writeUint32BE(_framerate); _dxa.writeUint16BE(_width); _dxa.writeUint16BE(_height); } void DxaEncoder::writeNULL() { //NULL _dxa.writeUint32LE(typeNULL); } void DxaEncoder::writeFrame(byte *frame, byte *palette) { if (_framecount == 0 || memcmp(_prevpalette, palette, 768)) { _dxa.writeUint32LE(typeCMAP); _dxa.write(palette, 768); memcpy(_prevpalette, palette, 768); } else { writeNULL(); } if (_framecount == 0 || memcmp(_prevframe, frame, _width * _workheight)) { //FRAM byte compType; _dxa.writeUint32LE(typeFRAM); if (_framecount == 0) compType = 2; else compType = 13; switch (compType) { case 2: { uLong outsize = _width * _workheight; byte *outbuf = new byte[outsize]; compress2(outbuf, &outsize, frame, _width * _workheight, 9); _dxa.writeByte(compType); _dxa.writeUint32BE(outsize); _dxa.write(outbuf, outsize); delete[] outbuf; break; } case 13: { int r; uLong frameoutsize; byte *frameoutbuf; byte *xorbuf = new byte[_width * _workheight]; uLong xorsize_z = _width * _workheight; byte *xorbuf_z = new byte[xorsize_z]; uLong rawsize_z = _width * _workheight; byte *rawbuf_z = new byte[rawsize_z]; uLong m13size = _width * _workheight * 2; byte *m13buf = new byte[m13size]; uLong m13size_z = _width * _workheight; byte *m13buf_z = new byte[m13size_z]; /* encode the delta frame with mode 12 */ m13size = m13encode(frame, m13buf); /* create the xor buffer */ for (int i = 0; i < _width * _workheight; i++) xorbuf[i] = _prevframe[i] ^ frame[i]; /* compress the m13 buffer */ compress2(m13buf_z, &m13size_z, m13buf, m13size, 9); /* compress the xor buffer */ xorsize_z = m13size_z; r = compress2(xorbuf_z, &xorsize_z, xorbuf, _width * _workheight, 9); if (r != Z_OK) xorsize_z = 0xFFFFFFF; if (m13size_z < xorsize_z) { compType = 13; frameoutsize = m13size_z; frameoutbuf = m13buf_z; } else { compType = 3; frameoutsize = xorsize_z; frameoutbuf = xorbuf_z; } /* compress the raw frame */ rawsize_z = frameoutsize; r = compress2(rawbuf_z, &rawsize_z, frame, _width * _workheight, 9); if (r != Z_OK) rawsize_z = 0xFFFFFFF; if (rawsize_z < frameoutsize) { compType = 2; frameoutsize = rawsize_z; frameoutbuf = rawbuf_z; } _dxa.writeByte(compType); _dxa.writeUint32BE(frameoutsize); _dxa.write(frameoutbuf, frameoutsize); delete[] xorbuf_z; delete[] rawbuf_z; delete[] m13buf_z; delete[] m13buf; delete[] xorbuf; break; } } memcpy(_prevframe, frame, _width * _workheight); } else { writeNULL(); } _framecount++; } bool DxaEncoder::m13blocksAreEqual(byte *frame, int x, int y, int x2, int y2, int w, int h) { byte *b1 = _prevframe + x + y * _width; byte *b2 = frame + x2 + y2 * _width; for (int yc = 0; yc < h; yc++) { if (memcmp(b1, b2, w)) return false; b1 += _width; b2 += _width; } return true; } bool DxaEncoder::m13blockIsSolidColor(byte *frame, int x, int y, int w, int h, byte &color) { byte *b2 = frame + x + y * _width; color = *b2; for (int yc = 0; yc < h; yc++) { for (int xc = 0; xc < w; xc++) { if (b2[xc] != color) return false; } b2 += _width; } return true; } void DxaEncoder::m13blockDelta(byte *frame, int x, int y, int x2, int y2, DiffStruct &diff) { byte *b1 = _prevframe + x + y * _width; byte *b2 = frame + x2 + y2 * _width; diff.count = 0; diff.map = 0; for (int yc = 0; yc < BLOCKH; yc++) { for (int xc = 0; xc < BLOCKW; xc++) { if (b1[xc] != b2[xc]) { diff.map = (diff.map << 1) | 1; diff.pixels[diff.count++] = b2[xc]; } else { diff.map = (diff.map << 1) | 0; } } b1 += _width; b2 += _width; } } bool DxaEncoder::m13motionVector(byte *frame, int x, int y, int w, int h, int &mx, int &my) { int xmin = (0 > x-7) ? 0 : x-7; int ymin = (0 > y-7) ? 0 : y-7; int xmax = (_width < x+8) ? _width : x+8; int ymax = (_workheight < y+8) ? _height : y+8; for (int yc = ymin; yc < ymax; yc++) { for (int xc = xmin; xc < xmax; xc++) { if (m13blocksAreEqual(frame, xc, yc, x, y, w, h)) { mx = xc - x; my = yc - y; return true; } } } return false; } int DxaEncoder::m13countColors(byte *block, byte *pixels, unsigned long &code, int &codeSize) { code = 0; codeSize = 0; /* count the number of colors used in this block */ int count = 0; int colTab[256]; for (int i = 0; i < 256; i++) colTab[i] = -1; for (int i = 0; i < BLOCKW * BLOCKH; i++) { if (colTab[block[i]] == -1) { colTab[block[i]] = count; pixels[count] = block[i]; count++; } } if (count <= 4) { /* set the bitmask */ if (count == 2) { for (int i = 15; i >= 0; i--) { code = (code << 1) | colTab[block[i]]; } codeSize = 2; } else if (count == 4 || count == 3) { for (int i = 15; i >= 0; i--) { code = (code << 2) | colTab[block[i]]; } codeSize = 4; } } return count; } /* grab the block */ void DxaEncoder::grabBlock(byte *frame, int x, int y, int blockw, int blockh, byte *block) { byte *b2 = (byte*)frame + x + y * _width; for (int yc = 0; yc < blockh; yc++) { memcpy(&block[yc*blockw], b2, blockw); b2 += _width; } } uLong DxaEncoder::m13encode(byte *frame, byte *outbuf) { byte *codeB = _codeBuf; byte *dataB = _dataBuf; byte *motB = _motBuf; byte *maskB = _maskBuf; byte *outb = outbuf; byte color; int mx, my; DiffStruct diff; memset(_codeBuf, 0, _width * _height / 16); memset(_dataBuf, 0, _width * _height); memset(_motBuf, 0, _width * _height); memset(_maskBuf, 0, _width * _height); for (int by = 0; by < _workheight; by += BLOCKH) { for (int bx = 0; bx < _width; bx += BLOCKW) { if (m13blocksAreEqual(frame, bx, by, bx, by, BLOCKW, BLOCKH)) { *codeB++ = 0; continue; } if (m13blockIsSolidColor(frame, bx, by, BLOCKW, BLOCKH, color)) { *codeB++ = 2; *dataB++ = color; continue; } if (m13motionVector(frame, bx, by, BLOCKW, BLOCKH, mx, my)) { /* motion vector */ byte motionByte = 0; if (mx < 0) motionByte |= 0x80; motionByte |= (abs(mx) & 7) << 4; if (my < 0) motionByte |= 0x08; motionByte |= abs(my) & 7; *codeB++ = 4; *motB++ = motionByte; continue; } byte subMask = 0; byte subMot[4], subData[16]; int subMotSize = 0, subDataSize = 0; static const int subX[4] = {0, 2, 0, 2}; static const int subY[4] = {0, 0, 2, 2}; /* 0: skip 1: solid color (+ data byte) 2: motion vector (+ mot byte) 3: raw block (+ 4 data bytes) */ for (int subBlock = 0; subBlock < 4; subBlock++) { int sx = bx + subX[subBlock], sy = by + subY[subBlock]; byte scolor; int smx, smy; if (m13blocksAreEqual(frame, sx, sy, sx, sy, BLOCKW/2, BLOCKH/2)) { subMask = (subMask << 2) | 0; continue; } if (m13blockIsSolidColor(frame, sx, sy, BLOCKW/2, BLOCKH/2, scolor)) { subData[subDataSize++] = scolor; subMask = (subMask << 2) | 1; continue; } if (m13motionVector(frame, sx, sy, BLOCKW/2, BLOCKH/2, smx, smy)) { byte motionByte = 0; if (smx < 0) motionByte |= 0x80; motionByte |= (abs(smx) & 7) << 4; if (smy < 0) motionByte |= 0x08; motionByte |= abs(smy) & 7; subMot[subMotSize++] = motionByte; subMask = (subMask << 2) | 2; continue; } byte *b2 = (byte*)frame + sx + sy * _width; for (int yc = 0; yc < BLOCKH/2; yc++) { memcpy(&subData[subDataSize], b2, BLOCKW/2); subDataSize += BLOCKW/2; b2 += _width; } subMask = (subMask << 2) | 3; } int blockSize = 0; m13blockDelta(frame, bx, by, bx, by, diff); byte block[16]; grabBlock(frame, bx, by, BLOCKW, BLOCKW, block); unsigned long code; int codeSize; byte pixels[16]; int count = m13countColors(block, pixels, code, codeSize); int countColorsSize = 1000; if (count == 2) countColorsSize = 4; // 2 bytes mask, 2 pixels else if (count <= 4) countColorsSize = 4 + count; // 4 bytes mask, count pixels if (countColorsSize < diff.count + 2) { blockSize = countColorsSize; } else { if (diff.count <= 12) { blockSize = 2 + diff.count; } else { blockSize = 16; } } if (1 + subMotSize + subDataSize < blockSize) { /* store subblocks */ *codeB++ = 8; *maskB++ = subMask; memcpy(dataB, subData, subDataSize); dataB += subDataSize; memcpy(motB, subMot, subMotSize); motB += subMotSize; } else { /* store full block */ if (countColorsSize < diff.count + 2) { /* write the pixel values */ *codeB++ = 30 + count; memcpy(dataB, pixels, count); dataB += count; if (codeSize == 2) { WRITE_BE_UINT16(maskB, (uint16)code); } else { WRITE_BE_UINT32(maskB, (uint16)code); } maskB += codeSize; } else { if (diff.count <= 12) { /* difference map */ *codeB++ = 1; WRITE_BE_UINT16(maskB, diff.map); maskB += 2; memcpy(dataB, diff.pixels, diff.count); dataB += diff.count; } else { /* write the whole block */ *codeB++ = 3; memcpy(dataB, block, 16); dataB += 16; } } } } } outb = outbuf; int size; size = dataB - _dataBuf; WRITE_BE_UINT32(outb, size); outb += 4; size = motB - _motBuf; WRITE_BE_UINT32(outb, size); outb += 4; size = maskB - _maskBuf; WRITE_BE_UINT32(outb, size); outb += 4; /* this size is always constant throughout a DXA */ memcpy(outb, _codeBuf, codeB - _codeBuf); outb += codeB - _codeBuf; memcpy(outb, _dataBuf, dataB - _dataBuf); outb += dataB - _dataBuf; memcpy(outb, _motBuf, motB - _motBuf); outb += motB - _motBuf; memcpy(outb, _maskBuf, maskB - _maskBuf); outb += maskB - _maskBuf; return outb - outbuf; } EncodeDXA::EncodeDXA(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) { ToolInput input; input.format = "*.*"; _inputPaths.push_back(input); _shorthelp = "Used to create DXA files from extracted Smacker archives."; _helptext = "Usage: " + getName() + " [mode] [mode-params] [-o outpufile = inputfile.san] \n" + "Output will be two files, one with .dxa extension and the other depending on the used audio codec."; } void EncodeDXA::execute() { int width, height, framerate, frames; ScaleMode scaleMode; Common::Filename inpath(_inputPaths[0].path); Common::Filename outpath(_outputPath); if (outpath.empty()) // Actual change of extension is done later... outpath = inpath; // check if the wav file exists. Common::Filename wavpath(inpath); wavpath.setExtension(".wav"); struct stat statinfo; if (!stat(wavpath.getFullPath().c_str(), &statinfo)) { outpath.setExtension(audio_extensions(_format)); convertWAV(&wavpath, &outpath); } // read some data from the Bink or Smacker file. readVideoInfo(&inpath, width, height, framerate, frames, scaleMode); print("Width = %d, Height = %d, Framerate = %d, Frames = %d", width, height, framerate, frames); // create the encoder object outpath.setExtension(".dxa"); DxaEncoder dxe(*this, outpath, width, height, framerate, scaleMode); // No sound block dxe.writeNULL(); uint8 *image = NULL; uint8 *palette = NULL; char fullname[1024]; strcpy(fullname, inpath.getFullPath().c_str()); // Check starting frame (binkconv starts at 0, ffmpeg starts at 1) int framenum = 0; char strbuf[1024]; sprintf(strbuf, "%s%04d.png", fullname, framenum); if (!Common::Filename(strbuf).exists()) framenum++; print("Encoding video..."); for (int f = 0; f < frames; f++) { if (frames > 999) sprintf(strbuf, "%s%04d.png", fullname, framenum); else if (frames > 99) sprintf(strbuf, "%s%03d.png", fullname, framenum); else if (frames > 9) sprintf(strbuf, "%s%02d.png", fullname, framenum); else sprintf(strbuf, "%s%d.png", fullname, framenum); inpath.setFullName(strbuf); int r = read_png_file(inpath.getFullPath().c_str(), image, palette, width, height); if (!palette) { error("8-bit 256-color image expected"); } if (!r) { if (scaleMode != S_NONE) { byte *unscaledImage = new byte[width * height / 2]; for (int y = 0; y < height; y += 2) memcpy(&unscaledImage[(width*y)/2], &image[width*y], width); dxe.writeFrame(unscaledImage, palette); delete[] unscaledImage; } else { dxe.writeFrame(image, palette); } } delete[] image; delete[] palette; if (r) break; framenum++; if (framenum % 20 == 0) { print("Encoding video...%d%% (%d of %d)", 100 * framenum / frames, framenum, frames); } } print("Encoding video...100%% (%d of %d)", frames, frames); } int EncodeDXA::read_png_file(const char* filename, unsigned char *&image, unsigned char *&palette, int &width, int &height) { png_byte header[8]; png_byte color_type; png_structp png_ptr; png_infop info_ptr; png_bytep *row_pointers; png_size_t rowbytes; Common::File fp(filename, "rb"); fp.read_throwsOnError(header, 8); if (png_sig_cmp(header, 0, 8)) return 1; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return 1; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) return 1; //if (setjmp(png_jmpbuf(png_ptr))) // return 1; png_init_io(png_ptr, fp.getFileHandle()); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); /*png_byte bit_depth =*/ png_get_bit_depth(png_ptr, info_ptr); if (color_type != PNG_COLOR_TYPE_PALETTE) { palette = NULL; return 2; } /*int number_of_passes =*/ png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); // read file //if (setjmp(png_jmpbuf(png_ptr))) // return 1; rowbytes = png_get_rowbytes(png_ptr, info_ptr); row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); for (int y=0; ygetFullPath().c_str(), false, -1, outpath->getFullPath().c_str(), _format); } #ifdef STANDALONE_MAIN int main(int argc, char *argv[]) { EncodeDXA encode_dxa(argv[0]); return encode_dxa.run(argc, argv); } #endif
X Tutup