void PLTFile::getColorRows(byte rows[4 * 256 * kLayerMAX], const uint8 colors[kLayerMAX]) { for (uint i = 0; i < kLayerMAX; i++, rows += 4 * 256) { ImageDecoder *palette = 0; try { palette = getLayerPalette(i, colors[i]); // The images have their origin at the bottom left, so we flip the color row const uint8 row = palette->getMipMap(0).height - 1 - colors[i]; // Copy the whole row into the buffer memcpy(rows, palette->getMipMap(0).data + (row * 4 * 256), 4 * 256); } catch (Common::Exception &e) { // On error set to pink (while honoring intensity), for high debug visibility for (uint32 p = 0; p < 256; p++) { rows[p * 4 + 0] = p; rows[p * 4 + 1] = 0x00; rows[p * 4 + 2] = p; rows[p * 4 + 3] = 0xFF; } e.add("Failed to load palette \"%s\"", kPalettes[i]); Common::printException(e, "WARNING: "); } delete palette; } }
/** Load a specific layer palette image and perform some sanity checks. */ ImageDecoder *PLTFile::getLayerPalette(uint32 layer, uint8 row) { assert(layer < kLayerMAX); // TODO: We may want to cache these somehow... ImageDecoder *palette = loadImage(kPalettes[layer]); try { if (palette->getFormat() != kPixelFormatBGRA) throw Common::Exception("Invalid format (%d)", palette->getFormat()); if (palette->getMipMapCount() < 1) throw Common::Exception("No mip maps"); const ImageDecoder::MipMap &mipMap = palette->getMipMap(0); if (mipMap.width != 256) throw Common::Exception("Invalid width (%d)", mipMap.width); if (row >= mipMap.height) throw Common::Exception("Invalid height (%d >= %d)", row, mipMap.height); } catch (...) { delete palette; throw; } return palette; }
void Cursor::load() { ::Aurora::FileType type; Common::SeekableReadStream *img = ResMan.getResource(::Aurora::kResourceCursor, _name, &type); if (!img) throw Common::Exception("No such cursor resource \"%s\"", _name.c_str()); _hotspotX = 0; _hotspotY = 0; ImageDecoder *image; // Loading the different image formats if (type == ::Aurora::kFileTypeTGA) image = new TGA(*img); else if (type == ::Aurora::kFileTypeDDS) image = new DDS(*img); else if (type == ::Aurora::kFileTypeCUR) { WinIconImage *cursor = new WinIconImage(*img); if (_hotspotX < 0) _hotspotX = cursor->getHotspotX(); if (_hotspotY < 0) _hotspotY = cursor->getHotspotY(); image = cursor; } else { delete img; throw Common::Exception("Unsupported cursor resource type %d", (int) type); } delete img; _width = image->getMipMap(0).width; _height = image->getMipMap(0).height; TXI txi; txi.getFeatures().filter = false; try { Texture *texture = new Texture(image, &txi); image = 0; try { _texture = TextureMan.add(texture, _name); } catch(...) { delete texture; throw; } } catch (...) { delete image; throw; } _hotspotX = CLIP(_hotspotX, 0, _width - 1); _hotspotY = CLIP(_hotspotY, 0, _height - 1); }
/* Create a tinted texture by combining the tint map with the tint colors. * * This is currently all done here in software and has several drawbacks: * - Slow * - We're creating lots of uncompressed RBGA texture, so it * takes up a lot of memory, both on the GPU and in system RAM * * TODO: We really need to do this in shaders in the future. */ void ModelNode_NWN2::createTint() { if (_tintMap.empty()) return; ImageDecoder *tintMap = 0; Surface *tintedMap = 0; try { // Load and uncompress the texture tintMap = Texture::loadImage(_tintMap); if (tintMap->isCompressed()) tintMap->decompress(); const ImageDecoder::MipMap &tintImg = tintMap->getMipMap(0); // Create a new target surface with the same dimensions tintedMap = new Surface(tintImg.width, tintImg.height); ImageDecoder::MipMap &tintedImg = tintedMap->getMipMap(); // Iterate over all pixels: read values, mix tints, write pixel to target surface for (int n = 0; n < tintImg.width * tintImg.height; n++) { float srcColor[4], dstColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; tintImg.getPixel(n, srcColor[0], srcColor[1], srcColor[2], srcColor[3]); if (srcColor[3] != 0.0f) { // Mix using the value and the alpha components as intensities // TODO: Verify how the mixing is actually done in NWN2! for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) dstColor[j] += _tint[i][j] * srcColor[i] * _tint[i][3] * srcColor[3] * _diffuse[i]; } } else // Source alpha is 0.0f: No tinting for this pixel for (int i = 0; i < 3; i++) dstColor[i] = _diffuse[i] * _tint[i][3]; tintedImg.setPixel(n, dstColor[0], dstColor[1], dstColor[2], dstColor[3]); } } catch (...) { delete tintMap; delete tintedMap; return; } delete tintMap; // And add the new texture to the TextureManager TextureHandle tintedTexture = TextureMan.add(Texture::create(tintedMap)); _textures.push_back(tintedTexture); _tintedMapIndex = _textures.size() - 1; }