void VisualText::createTexture() { Common::CodePage codePage = StarkSettings->getTextCodePage(); Common::U32String unicodeText = Common::convertToU32String(_text.c_str(), codePage); // Get the font and required metrics const Graphics::Font *font = StarkFontProvider->getScaledFont(_fontType, _fontCustomIndex); uint scaledLineHeight = StarkFontProvider->getScaledFontHeight(_fontType, _fontCustomIndex); uint originalLineHeight = StarkFontProvider->getOriginalFontHeight(_fontType, _fontCustomIndex); uint maxScaledLineWidth = StarkGfx->scaleWidthOriginalToCurrent(_targetWidth); // Word wrap the text Common::Array<Common::U32String> lines; font->wordWrapText(unicodeText, maxScaledLineWidth, lines); // Use the actual font bounding box to prevent text from being cut off Common::Rect scaledRect; if (!lines.empty()) { scaledRect = font->getBoundingBox(lines[0]); for (uint i = 1; i < lines.size(); i++) { scaledRect.extend(font->getBoundingBox(lines[i], 0, scaledLineHeight * i)); } } // Make sure lines have approximately consistent height regardless of the characters they use scaledRect.bottom = MAX<int16>(scaledRect.bottom, scaledLineHeight * lines.size()); if (!isBlank()) { _originalRect.right = StarkGfx->scaleWidthCurrentToOriginal(scaledRect.right); _originalRect.bottom = originalLineHeight * lines.size(); } else { // For Empty text, preserve the original width and height for being used as clicking area _originalRect.right = _targetWidth; _originalRect.bottom = _targetHeight; } // Create a surface to render to Graphics::Surface surface; surface.create(scaledRect.right, scaledRect.bottom, Gfx::Driver::getRGBAPixelFormat()); uint32 color = surface.format.ARGBToColor( _color.a, _color.r, _color.g, _color.b ); uint32 bgColor = surface.format.ARGBToColor( _backgroundColor.a, _backgroundColor.r, _backgroundColor.g, _backgroundColor.b ); surface.fillRect(Common::Rect(surface.w, surface.h), bgColor); // Render the lines to the surface for (uint i = 0; i < lines.size(); i++) { font->drawString(&surface, lines[i], 0, scaledLineHeight * i, surface.w, color, _align); } // Create a texture from the surface _texture = _gfx->createTexture(&surface); _texture->setSamplingFilter(Gfx::Texture::kNearest); surface.free(); }
void MacFontManager::generateFontSubstitute(MacFont &macFont) { Common::String name; // First we try twice size name = getFontName(macFont.getId(), macFont.getSize() * 2, macFont.getSlant()); if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) { generateFont(macFont, *_fontRegistry[name]); return; } // Now half size name = getFontName(macFont.getId(), macFont.getSize() / 2, macFont.getSlant()); if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) { generateFont(macFont, *_fontRegistry[name]); return; } // No simple substitute was found. Looking for neighborhood fonts // First we gather all font sizes for this font Common::Array<int> sizes; for (Common::HashMap<Common::String, MacFont *>::iterator i = _fontRegistry.begin(); i != _fontRegistry.end(); ++i) { if (i->_value->getId() == macFont.getId() && i->_value->getSlant() == macFont.getSlant() && !i->_value->isGenerated()) sizes.push_back(i->_value->getSize()); } if (sizes.empty()) { warning("No viable substitute found for font %s", getFontName(macFont)); return; } // Now looking next larger font, and store the largest one for next check int candidate = 1000; int maxSize = sizes[0]; for (uint i = 0; i < sizes.size(); i++) { if (sizes[i] > macFont.getSize() && sizes[i] < candidate) candidate = sizes[i]; if (sizes[i] > maxSize) maxSize = sizes[i]; } if (candidate != 1000) { generateFont(macFont, *_fontRegistry[getFontName(macFont.getId(), candidate, macFont.getSlant())]); return; } // Now next smaller font, which is the biggest we have generateFont(macFont, *_fontRegistry[getFontName(macFont.getId(), maxSize, macFont.getSlant())]); }
void DefaultSaveFileManager::assureCached(const Common::String &savePathName) { // Check that path exists and is usable. checkPath(Common::FSNode(savePathName)); #ifdef USE_LIBCURL Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing if (!files.empty()) updateSavefilesList(files); //makes this cache invalid else _lockedFiles = files; #endif if (_cachedDirectory == savePathName) { return; } _saveFileCache.clear(); _cachedDirectory.clear(); if (getError().getCode() != Common::kNoError) { warning("DefaultSaveFileManager::assureCached: Can not cache path '%s': '%s'", savePathName.c_str(), getErrorDesc().c_str()); return; } // FSNode can cache its members, thus create it after checkPath to reflect // actual file system state. const Common::FSNode savePath(savePathName); Common::FSList children; if (!savePath.getChildren(children, Common::FSNode::kListFilesOnly)) { return; } // Build the savefile name cache. for (Common::FSList::const_iterator file = children.begin(), end = children.end(); file != end; ++file) { if (_saveFileCache.contains(file->getName())) { warning("DefaultSaveFileManager::assureCached: Name clash when building cache, ignoring file '%s'", file->getName().c_str()); } else { _saveFileCache[file->getName()] = *file; } } // Only now store that we cached 'savePathName' to indicate we successfully // cached the directory. _cachedDirectory = savePathName; }
Graphics::Surface *CDToonsDecoder::decodeImage(Common::SeekableReadStream *stream) { uint16 u0 = stream->readUint16BE(); // always 9? uint16 frameId = stream->readUint16BE(); uint16 blocksValidUntil = stream->readUint16BE(); byte u6 = stream->readByte(); byte backgroundColor = stream->readByte(); debugN(5, "CDToons frame %d, size %d, unknown %04x (at 0), blocks valid until %d, unknown 6 is %02x, bkg color is %02x\n", frameId, stream->size(), u0, blocksValidUntil, u6, backgroundColor); Common::Rect clipRect = readRect(stream); debugN(9, "CDToons clipRect: (%d, %d) to (%d, %d)\n", clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); Common::Rect dirtyRect = readRect(stream); debugN(9, "CDToons dirtyRect: (%d, %d) to (%d, %d)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom); uint32 flags = stream->readUint32BE(); if (flags & 0x80) error("CDToons: frame already processed?"); debugN(5, "CDToons flags: %08x\n", flags); uint16 blockCount = stream->readUint16BE(); uint16 blockOffset = stream->readUint16BE(); debugN(9, "CDToons: %d blocks at 0x%04x\n", blockCount, blockOffset); // max block id? uint16 u32 = stream->readUint16BE(); debugN(5, "CDToons unknown at 32: %04x\n", u32); byte actionCount = stream->readByte(); byte u35 = stream->readByte(); uint16 paletteId = stream->readUint16BE(); byte paletteSet = stream->readByte(); debugN(9, "CDToons palette id %04x, palette byte %02x\n", paletteId, paletteSet); byte u39 = stream->readByte(); uint16 u40 = stream->readUint16BE(); uint16 u42 = stream->readUint16BE(); debugN(5, "CDToons: unknown at 35 is %02x, unknowns at 39: %02x, %04x, %04x\n", u35, u39, u40, u42); Common::Array<CDToonsAction> actions; for (uint i = 0; i < actionCount; i++) { CDToonsAction action; action.blockId = stream->readUint16BE(); action.rect = readRect(stream); debugN(9, "CDToons action: render block %d at (%d, %d) to (%d, %d)\n", action.blockId, action.rect.left, action.rect.top, action.rect.right, action.rect.bottom); actions.push_back(action); } if (stream->pos() > blockOffset) error("CDToons header ended at 0x%08x, but blocks should have started at 0x%08x", stream->pos(), blockOffset); if (stream->pos() != blockOffset) error("CDToons had %d unknown bytes after header", blockOffset - stream->pos()); for (uint i = 0; i < blockCount; i++) { uint16 blockId = stream->readUint16BE(); if (blockId >= 1200) error("CDToons: block id %d was too high", blockId); if (_blocks.contains(blockId)) error("CDToons: new block %d was already seen", blockId); CDToonsBlock block; block.flags = stream->readUint16BE(); // flag 1 = palette, flag 2 = data? if (block.flags & 0x8000) error("CDToons: block already processed?"); block.size = stream->readUint32BE(); if (block.size < 14) error("CDToons: block size was %d, too small", block.size); block.size -= 14; block.startFrame = stream->readUint16BE(); block.endFrame = stream->readUint16BE(); block.unknown12 = stream->readUint16BE(); block.data = new byte[block.size]; stream->read(block.data, block.size); debugN(9, "CDToons block id 0x%04x of size 0x%08x, flags %04x, from frame %d to %d, unknown at 12 is %04x\n", blockId, block.size, block.flags, block.startFrame, block.endFrame, block.unknown12); _blocks[blockId] = block; } byte xFrmBegin = 0, xFrmCount; Common::Array<CDToonsDiff> diffs; while (true) { int32 nextPos = stream->pos(); uint32 tag = stream->readUint32BE(); uint32 size = stream->readUint32BE(); nextPos += size; switch (tag) { case MKTAG('D','i','f','f'): { debugN(5, "CDToons: Diff\n"); uint16 count = stream->readUint16BE(); Common::Rect diffClipRect = readRect(stream); debugN(9, "CDToons diffClipRect: (%d, %d) to (%d, %d)\n", diffClipRect.left, diffClipRect.top, diffClipRect.right, diffClipRect.bottom); debugN(5, "CDToons Diff: %d subentries\n", count); for (uint i = 0; i < count; i++) { CDToonsDiff diff; diff.rect = readRect(stream); diff.size = stream->readUint32BE(); if (diff.size < 20) error("CDToons: Diff block size was %d, too small", diff.size); uint16 diffWidth = stream->readUint16BE(); uint16 diffHeight = stream->readUint16BE(); uint16 unknown16 = stream->readUint16BE(); uint16 unknown18 = stream->readUint16BE(); diff.size -= 8; if (diffWidth != diff.rect.width() || diffHeight != diff.rect.height()) error("CDToons: Diff sizes didn't match"); debugN(5, "CDToons Diff: size %d, frame from (%d, %d) to (%d, %d), unknowns %04x, %04x\n", diff.size, diff.rect.left, diff.rect.top, diff.rect.right, diff.rect.bottom, unknown16, unknown18); diff.data = new byte[diff.size]; stream->read(diff.data, diff.size); diffs.push_back(diff); } } break; case MKTAG('X','F','r','m'): { debugN(5, "CDToons: XFrm\n"); if (!(flags & 0x10)) error("CDToons: useless XFrm?"); if (xFrmBegin) error("CDToons: duplicate XFrm"); xFrmBegin = stream->readByte(); xFrmCount = stream->readByte(); debugN(9, "CDToons XFrm: run %d actions from %d\n", xFrmCount, xFrmBegin - 1); // TODO: don't ignore (if xFrmCount is non-zero) Common::Rect dirtyRectXFrm = readRect(stream); debugN(9, "CDToons XFrm dirtyRect: (%d, %d) to (%d, %d)\n", dirtyRectXFrm.left, dirtyRectXFrm.top, dirtyRectXFrm.right, dirtyRectXFrm.bottom); // always zero? Common::Rect dirtyRect2XFrm = readRect(stream); debugN(9, "CDToons XFrm dirtyRect2: (%d, %d) to (%d, %d)\n", dirtyRect2XFrm.left, dirtyRect2XFrm.top, dirtyRect2XFrm.right, dirtyRect2XFrm.bottom); } break; case MKTAG('M','r','k','s'): debugN(5, "CDToons: Mrks\n"); if (!(flags & 0x2)) error("CDToons: useless Mrks?"); // TODO warning("CDToons: encountered Mrks, not implemented yet"); break; case MKTAG('S','c','a','l'): // TODO warning("CDToons: encountered Scal, not implemented yet"); break; case MKTAG('W','r','M','p'): warning("CDToons: encountered WrMp, ignoring"); break; case MKTAG('F','r','t','R'): { debugN(5, "CDToons: FrtR\n"); if (!(flags & 0x40)) error("CDToons: useless FrtR?"); uint16 count = stream->readUint16BE(); debugN(9, "CDToons FrtR: %d dirty rectangles\n", count); for (uint i = 0; i < count; i++) { Common::Rect dirtyRectFrtR = readRect(stream); debugN(9, "CDToons FrtR dirtyRect: (%d, %d) to (%d, %d)\n", dirtyRectFrtR.left, dirtyRectFrtR.top, dirtyRectFrtR.right, dirtyRectFrtR.bottom); } } break; case MKTAG('B','c','k','R'): { debugN(5, "CDToons: BckR\n"); if (!(flags & 0x20)) error("CDToons: useless BckR?"); uint16 count = stream->readUint16BE(); debugN(9, "CDToons BckR: %d subentries\n", count); for (uint i = 0; i < count; i++) { Common::Rect dirtyRectBckR = readRect(stream); debugN(9, "CDToons BckR dirtyRect: (%d, %d) to (%d, %d)\n", dirtyRectBckR.left, dirtyRectBckR.top, dirtyRectBckR.right, dirtyRectBckR.bottom); } } break; default: warning("Unknown CDToons tag '%s'", tag2str(tag)); } if (stream->pos() > nextPos) error("CDToons ran off the end of a block while reading it (at %d, next block at %d)", stream->pos(), nextPos); if (stream->pos() != nextPos) { warning("CDToons had %d unknown bytes after block", nextPos - stream->pos()); stream->seek(nextPos); } if (stream->pos() == stream->size()) break; } for (uint i = 0; i < diffs.size(); i++) { renderBlock(diffs[i].data, diffs[i].size, diffs[i].rect.left, diffs[i].rect.top, diffs[i].rect.width(), diffs[i].rect.height()); delete[] diffs[i].data; } if (!diffs.empty()) return _surface; for (uint i = 0; i < actions.size(); i++) { CDToonsAction &action = actions[i]; if (i == 0 && action.blockId == 0) memset(_surface->pixels, backgroundColor, _surface->w * _surface->h); if (!_blocks.contains(action.blockId)) continue; if (!action.rect.right) continue; if (i == 0 && !diffs.empty()) continue; CDToonsBlock &block = _blocks[action.blockId]; uint16 width = READ_BE_UINT16(block.data + 2); uint16 height = READ_BE_UINT16(block.data); renderBlock(block.data + 14, block.size - 14, action.rect.left, action.rect.top, width, height); } if (paletteId && _currentPaletteId != paletteId) { if (!_blocks.contains(paletteId)) error("CDToons: no block for palette %04x", paletteId); if (_blocks[paletteId].size != 2 * 3 * 256) error("CDToons: palette %04x is wrong size (%d)", paletteId, _blocks[paletteId].size); _currentPaletteId = paletteId; if (!paletteSet) setPalette(_blocks[paletteId].data); } return _surface; }