/** * Adds velocities and creates clipping rectangles for all the * objects that have moved on the specified object list. * @param pObjList Playfield display list to draw * @param pWin Playfield window top left position * @param pClip Playfield clipping rectangle * @param bNoVelocity When reset, objects pos is updated with velocity * @param bScrolled) When set, playfield has scrolled */ void FindMovingObjects(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip, bool bNoVelocity, bool bScrolled) { OBJECT *pObj; // object list traversal pointer for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) { if (!bNoVelocity) { // we want to add velocities to objects position if (bScrolled) { // this playfield has scrolled // indicate change pObj->flags |= DMA_CHANGED; } } if ((pObj->flags & DMA_CHANGED) || // object changed HasPalMoved(pObj->pPal)) { // or palette moved // object has changed in some way Common::Rect rcClip; // objects clipped bounding rectangle Common::Rect rcObj; // objects bounding rectangle // calc intersection of objects previous bounding rectangle // NOTE: previous position is in screen co-ords if (IntersectRectangle(rcClip, pObj->rcPrev, *pClip)) { // previous position is within clipping rect AddClipRect(rcClip); } // calc objects current bounding rectangle if (pObj->flags & DMA_ABS) { // object position is absolute rcObj.left = fracToInt(pObj->xPos); rcObj.top = fracToInt(pObj->yPos); } else { // object position is relative to window rcObj.left = fracToInt(pObj->xPos) - pWin->x; rcObj.top = fracToInt(pObj->yPos) - pWin->y; } rcObj.right = rcObj.left + pObj->width; rcObj.bottom = rcObj.top + pObj->height; // calc intersection of object with clipping rect if (IntersectRectangle(rcClip, rcObj, *pClip)) { // current position is within clipping rect AddClipRect(rcClip); // update previous position pObj->rcPrev = rcClip; } else { // clear previous position pObj->rcPrev = Common::Rect(); } // clear changed flag pObj->flags &= ~DMA_CHANGED; } } }
/** * Returns the x,y position of an objects animation point. * @param pObj Pointer to object * @param pPosX Gets set to objects X animation position * @param pPosY Gets set to objects Y animation position */ void GetAniPosition(OBJECT *pObj, int *pPosX, int *pPosY) { // validate object pointer assert(isValidObject(pObj)); // get the animation offset of the object GetAniOffset(pObj->hImg, pObj->flags, pPosX, pPosY); // from animation offset and objects position - determine objects animation point *pPosX += fracToInt(pObj->xPos); *pPosY += fracToInt(pObj->yPos); }
void PlayfieldGetPos(int which, int *pXpos, int *pYpos) { PLAYFIELD *pPlayfield; // pointer to relavent playfield // make sure there is a background assert(pCurBgnd != NULL); // make sure the playfield number is in range assert(which >= 0 && which < pCurBgnd->numPlayfields); // get playfield pointer pPlayfield = pCurBgnd->fieldArray + which; // get current integer position *pXpos = fracToInt(pPlayfield->fieldX); *pYpos = fracToInt(pPlayfield->fieldY); }
void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { _outputScreenWidth = width; _outputScreenHeight = height; // Setup backbuffer size. _backBuffer.setDimensions(width, height); uint overlayWidth = width; uint overlayHeight = height; // WORKAROUND: We can only support surfaces up to the maximum supported // texture size. Thus, in case we encounter a physical size bigger than // this maximum texture size we will simply use an overlay as big as // possible and then scale it to the physical display size. This sounds // bad but actually all recent chips should support full HD resolution // anyway. Thus, it should not be a real issue for modern hardware. if ( overlayWidth > (uint)g_context.maxTextureSize || overlayHeight > (uint)g_context.maxTextureSize) { const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; if (outputAspect > (frac_t)FRAC_ONE) { overlayWidth = g_context.maxTextureSize; overlayHeight = intToFrac(overlayWidth) / outputAspect; } else { overlayHeight = g_context.maxTextureSize; overlayWidth = fracToInt(overlayHeight * outputAspect); } } // HACK: We limit the minimal overlay size to 256x200, which is the // minimum of the dimensions of the two resolutions 256x240 (NES) and // 320x200 (many DOS games use this). This hopefully assure that our // GUI has working layouts. overlayWidth = MAX<uint>(overlayWidth, 256); overlayHeight = MAX<uint>(overlayHeight, 200); if (!_overlay || _overlay->getFormat() != _defaultFormatAlpha) { delete _overlay; _overlay = nullptr; _overlay = createSurface(_defaultFormatAlpha); assert(_overlay); // We always filter the overlay with GL_LINEAR. This assures it's // readable in case it needs to be scaled and does not affect it // otherwise. _overlay->enableLinearFiltering(true); } _overlay->allocate(overlayWidth, overlayHeight); _overlay->fill(0); // Re-setup the scaling for the screen and cursor recalculateDisplayArea(); recalculateCursorScaling(); // Something changed, so update the screen change ID. ++_screenChangeID; }
/** * Init a ANIM structure for single stepping through a animation script. * @param pAnim Animation data structure * @param pAniObj Object to animate * @param hNewScript Script of multipart frames * @param aniSpeed Sets speed of animation in frames */ void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) { OBJECT *pObj; // multi-object list iterator debugC(DEBUG_DETAILED, kTinselDebugAnimations, "InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph", !pAniObj ? 0 : fracToInt(pAniObj->xPos), !pAniObj ? 0 : fracToInt(pAniObj->yPos), !pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (byte *)pAnim); pAnim->aniDelta = 1; // will animate on next call to NextAnimRate pAnim->pObject = pAniObj; // set object to animate pAnim->hScript = hNewScript; // set animation script pAnim->scriptIndex = 0; // start of script pAnim->aniRate = aniSpeed; // set speed of animation // reset flip flags for the object - let the script do the flipping for (pObj = pAniObj; pObj != NULL; pObj = pObj->pSlave) { AnimateObjectFlags(pObj, pObj->flags & ~(DMA_FLIPH | DMA_FLIPV), pObj->hImg); } }
int MultiLeftmost(OBJECT *pMulti) { int left; // validate object pointer assert(pMulti >= objectList && pMulti <= objectList + NUM_OBJECTS - 1); // init leftmost point to first object left = fracToInt(pMulti->xPos); // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->xPos) < left) // this object is further left left = fracToInt(pMulti->xPos); } } // return left-most point return left; }
int MultiRightmost(OBJECT *pMulti) { int right; // validate object pointer assert(pMulti >= objectList && pMulti <= objectList + NUM_OBJECTS - 1); // init right-most point to first object right = fracToInt(pMulti->xPos) + pMulti->width; // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->xPos) + pMulti->width > right) // this object is further right right = fracToInt(pMulti->xPos) + pMulti->width; } } // return right-most point return right - 1; }
int MultiHighest(OBJECT *pMulti) { int highest; // validate object pointer assert(isValidObject(pMulti)); // init highest point to first object highest = fracToInt(pMulti->yPos); // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->yPos) < highest) // this object is higher highest = fracToInt(pMulti->yPos); } } // return highest point return highest; }
int MultiLowest(OBJECT *pMulti) { int lowest; // validate object pointer assert(pMulti >= objectList && pMulti <= objectList + NUM_OBJECTS - 1); // init lowest point to first object lowest = fracToInt(pMulti->yPos) + pMulti->height; // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->yPos) + pMulti->height > lowest) // this object is lower lowest = fracToInt(pMulti->yPos) + pMulti->height; } } // return lowest point return lowest - 1; }
int PlayfieldGetCentreX(int which) { PLAYFIELD *pPlayfield; // pointer to relavent playfield // make sure there is a background assert(pCurBgnd != NULL); // make sure the playfield number is in range assert(which >= 0 && which < pCurBgnd->numPlayfields); // get playfield pointer pPlayfield = pCurBgnd->fieldArray + which; // get current integer position return fracToInt(pPlayfield->fieldX) + SCREEN_WIDTH/2; }
inline int mixBuffer(int16 *&buf, const int8 *data, Paula::Offset &offset, frac_t rate, int neededSamples, size_t bufSize, size_t volume, uint8 panning) { int samples; for (samples = 0; samples < neededSamples && offset.int_off < bufSize; ++samples) { const int32 tmp = (int32) (((int32) data[offset.int_off]) * volume); if (stereo) { *buf++ += (tmp * (255 - panning)) >> 7; *buf++ += (tmp * (panning)) >> 7; } else *buf++ += tmp; // Step to next source sample offset.rem_off += rate; if (offset.rem_off >= (frac_t)FRAC_ONE) { offset.int_off += fracToInt(offset.rem_off); offset.rem_off &= FRAC_LO_MASK; } }
void ListWidget::reflowLayout() { Widget::reflowLayout(); _leftPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Left", 0); _rightPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Right", 0); _topPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Top", 0); _bottomPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Bottom", 0); _hlLeftPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.hlLeftPadding", 0); _hlRightPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.hlRightPadding", 0); _scrollBarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0); // HACK: Once we take padding into account, there are times where // integer rounding leaves a big chunk of white space in the bottom // of the list. // We do a rough rounding on the decimal places of Entries Per Page, // to add another entry even if it goes a tad over the padding. frac_t entriesPerPage = intToFrac(_h - _topPadding - _bottomPadding) / kLineHeight; // Our threshold before we add another entry is 0.9375 (0xF000 with FRAC_BITS being 16). const frac_t threshold = intToFrac(15) / 16; if ((frac_t)(entriesPerPage & FRAC_LO_MASK) >= threshold) entriesPerPage += FRAC_ONE; _entriesPerPage = fracToInt(entriesPerPage); assert(_entriesPerPage > 0); delete[] _textWidth; _textWidth = new int[_entriesPerPage]; for (int i = 0; i < _entriesPerPage; i++) _textWidth[i] = 0; if (_scrollBar) { _scrollBar->resize(_w - _scrollBarWidth + 1, 0, _scrollBarWidth, _h); scrollBarRecalc(); scrollToCurrent(); } }
void OpenGLGraphicsManager::recalculateDisplayArea() { if (!_gameScreen || _outputScreenHeight == 0) { return; } const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; const frac_t desiredAspect = getDesiredGameScreenAspect(); _displayWidth = _outputScreenWidth; _displayHeight = _outputScreenHeight; // Adjust one dimension for mantaining the aspect ratio. if (outputAspect < desiredAspect) { _displayHeight = intToFrac(_displayWidth) / desiredAspect; } else if (outputAspect > desiredAspect) { _displayWidth = fracToInt(_displayHeight * desiredAspect); } // We center the screen in the middle for now. _displayX = (_outputScreenWidth - _displayWidth ) / 2; _displayY = (_outputScreenHeight - _displayHeight) / 2; }
bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const { #ifdef SCUMM_LITTLE_ENDIAN if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888 #else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 #endif glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565 glIntFormat = GL_RGB; glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGBA5551 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; #if !USE_FORCED_GLES && !USE_FORCED_GLES2 // The formats below are not supported by every GLES implementation. // Thus, we do not mark them as supported when a GLES context is setup. } else if (isGLESContext()) { return false; #ifdef SCUMM_LITTLE_ENDIAN } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_INT_8_8_8_8; return true; #endif } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 glIntFormat = GL_RGB; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_1_5_5_5_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_4_4_4_4_REV; return true; #ifdef SCUMM_BIG_ENDIAN } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_INT_8_8_8_8_REV; return true; #endif } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565 glIntFormat = GL_RGB; glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_5_5_5_1; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; #endif // !USE_FORCED_GLES && !USE_FORCED_GLES2 } else { return false; } } frac_t OpenGLGraphicsManager::getDesiredGameScreenAspect() const { const uint width = _currentState.gameWidth; const uint height = _currentState.gameHeight; if (_currentState.aspectRatioCorrection) { // In case we enable aspect ratio correction we force a 4/3 ratio. // But just for 320x200 and 640x400 games, since other games do not need // this. if ((width == 320 && height == 200) || (width == 640 && height == 400)) { return intToFrac(4) / 3; } } return intToFrac(width) / height; } void OpenGLGraphicsManager::recalculateDisplayArea() { if (!_gameScreen || _outputScreenHeight == 0) { return; } const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; const frac_t desiredAspect = getDesiredGameScreenAspect(); _displayWidth = _outputScreenWidth; _displayHeight = _outputScreenHeight; // Adjust one dimension for mantaining the aspect ratio. if (outputAspect < desiredAspect) { _displayHeight = intToFrac(_displayWidth) / desiredAspect; } else if (outputAspect > desiredAspect) { _displayWidth = fracToInt(_displayHeight * desiredAspect); } // We center the screen in the middle for now. _displayX = (_outputScreenWidth - _displayWidth ) / 2; _displayY = (_outputScreenHeight - _displayHeight) / 2; // Setup drawing limitation for game graphics. // This invovles some trickery because OpenGL's viewport coordinate system // is upside down compared to ours. _backBuffer.setScissorBox(_displayX, _outputScreenHeight - _displayHeight - _displayY, _displayWidth, _displayHeight); // Clear the whole screen for the first three frames to remove leftovers. _scissorOverride = 3; // Update the cursor position to adjust for new display area. setMousePosition(_cursorX, _cursorY); // Force a redraw to assure screen is properly redrawn. _forceRedraw = true; } void OpenGLGraphicsManager::updateCursorPalette() { if (!_cursor || !_cursor->hasPalette()) { return; } if (_cursorPaletteEnabled) { _cursor->setPalette(0, 256, _cursorPalette); } else { _cursor->setPalette(0, 256, _gamePalette); } _cursor->setColorKey(_cursorKeyColor); } void OpenGLGraphicsManager::recalculateCursorScaling() { if (!_cursor || !_gameScreen) { return; } // By default we use the unscaled versions. _cursorHotspotXScaled = _cursorHotspotX; _cursorHotspotYScaled = _cursorHotspotY; _cursorWidthScaled = _cursor->getWidth(); _cursorHeightScaled = _cursor->getHeight(); // In case scaling is actually enabled we will scale the cursor according // to the game screen. if (!_cursorDontScale) { const frac_t screenScaleFactorX = intToFrac(_displayWidth) / _gameScreen->getWidth(); const frac_t screenScaleFactorY = intToFrac(_displayHeight) / _gameScreen->getHeight(); _cursorHotspotXScaled = fracToInt(_cursorHotspotXScaled * screenScaleFactorX); _cursorWidthScaled = fracToInt(_cursorWidthScaled * screenScaleFactorX); _cursorHotspotYScaled = fracToInt(_cursorHotspotYScaled * screenScaleFactorY); _cursorHeightScaled = fracToInt(_cursorHeightScaled * screenScaleFactorY); } } #ifdef USE_OSD const Graphics::Font *OpenGLGraphicsManager::getFontOSD() { return FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont); } #endif void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const { const uint width = _outputScreenWidth; const uint height = _outputScreenHeight; // A line of a BMP image must have a size divisible by 4. // We calculate the padding bytes needed here. // Since we use a 3 byte per pixel mode, we can use width % 4 here, since // it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the // usual way of computing the padding bytes required). const uint linePaddingSize = width % 4; const uint lineSize = width * 3 + linePaddingSize; // Allocate memory for screenshot uint8 *pixels = new uint8[lineSize * height]; // Get pixel data from OpenGL buffer GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels)); // BMP stores as BGR. Since we can't assume that GL_BGR is supported we // will swap the components from the RGB we read to BGR on our own. for (uint y = height; y-- > 0;) { uint8 *line = pixels + y * lineSize; for (uint x = width; x > 0; --x, line += 3) { SWAP(line[0], line[2]); } } // Open file Common::DumpFile out; out.open(filename); // Write BMP header out.writeByte('B'); out.writeByte('M'); out.writeUint32LE(height * lineSize + 54); out.writeUint32LE(0); out.writeUint32LE(54); out.writeUint32LE(40); out.writeUint32LE(width); out.writeUint32LE(height); out.writeUint16LE(1); out.writeUint16LE(24); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); // Write pixel data to BMP out.write(pixels, lineSize * height); // Free allocated memory delete[] pixels; } } // End of namespace OpenGL
/** * Redraws all objects within this clipping rectangle. * @param pObjList Object list to draw * @param pWin Window top left position * @param pClip Pointer to clip rectangle */ void UpdateClipRect(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip) { int x, y, right, bottom; // object corners int hclip, vclip; // total size of object clipping DRAWOBJECT currentObj; // filled in to draw the current object in list OBJECT *pObj; // object list iterator // Initialize the fields of the drawing object to empty memset(¤tObj, 0, sizeof(DRAWOBJECT)); for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) { if (pObj->flags & DMA_ABS) { // object position is absolute x = fracToInt(pObj->xPos); y = fracToInt(pObj->yPos); } else { // object position is relative to window x = fracToInt(pObj->xPos) - pWin->x; y = fracToInt(pObj->yPos) - pWin->y; } // calc object right right = x + pObj->width; if (right < 0) // totally clipped if negative continue; // calc object bottom bottom = y + pObj->height; if (bottom < 0) // totally clipped if negative continue; // bottom clip = low right y - clip low right y currentObj.botClip = bottom - pClip->bottom; if (currentObj.botClip < 0) { // negative - object is not clipped currentObj.botClip = 0; } // right clip = low right x - clip low right x currentObj.rightClip = right - pClip->right; if (currentObj.rightClip < 0) { // negative - object is not clipped currentObj.rightClip = 0; } // top clip = clip top left y - top left y currentObj.topClip = pClip->top - y; if (currentObj.topClip < 0) { // negative - object is not clipped currentObj.topClip = 0; } else { // clipped - adjust start position to top of clip rect y = pClip->top; } // left clip = clip top left x - top left x currentObj.leftClip = pClip->left - x; if (currentObj.leftClip < 0) { // negative - object is not clipped currentObj.leftClip = 0; } else { // NOTE: This else statement is disabled in tinsel v1 // clipped - adjust start position to left of clip rect x = pClip->left; } // calc object total horizontal clipping hclip = currentObj.leftClip + currentObj.rightClip; // calc object total vertical clipping vclip = currentObj.topClip + currentObj.botClip; if (hclip + vclip != 0) { // object is clipped in some way if (pObj->width <= hclip) // object totally clipped horizontally - ignore continue; if (pObj->height <= vclip) // object totally clipped vertically - ignore continue; // set clip bit in objects flags currentObj.flags = pObj->flags | DMA_CLIP; } else { // object is not clipped - copy flags currentObj.flags = pObj->flags; } // copy objects properties to local object currentObj.width = pObj->width; currentObj.height = pObj->height; currentObj.xPos = (short)x; currentObj.yPos = (short)y; currentObj.pPal = pObj->pPal; currentObj.constant = pObj->constant; currentObj.hBits = pObj->hBits; // draw the object DrawObject(¤tObj); } }
/** * Fill output buffer by advancing the generators for a 1/60th of a second. * @return Number of generated samples */ uint SoundGen2GS::generateOutput() { memset(_out, 0, _outSize * 2 * 2); if (!_playing || _playingSound == -1) return _outSize * 2; int16 *p = _out; int n = _outSize; while (n--) { int outl = 0; int outr = 0; for (int k = 0; k < MAX_GENERATORS; k++) { IIgsGenerator *g = &_generators[k]; if (!g->ins) continue; const IIgsInstrumentHeader *i = g->ins; // Advance envelope int vol = fracToInt(g->a); if (g->a <= i->env[g->seg].bp) { g->a += i->env[g->seg].inc * ENVELOPE_COEF; if (g->a > i->env[g->seg].bp) { g->a = i->env[g->seg].bp; g->seg++; } } else { g->a -= i->env[g->seg].inc * ENVELOPE_COEF; if (g->a < i->env[g->seg].bp) { g->a = i->env[g->seg].bp; g->seg++; } } // TODO: Advance vibrato here. The Apple IIGS uses a LFO with // triangle wave to modulate the frequency of both oscillators. // In Apple IIGS the vibrato and the envelope are updated at the // same time, so the vibrato speed depends on ENVELOPE_COEF. // Advance oscillators int s0 = 0; int s1 = 0; if (!g->osc[0].halt) { s0 = g->osc[0].base[fracToInt(g->osc[0].p)]; g->osc[0].p += g->osc[0].pd; if ((uint)fracToInt(g->osc[0].p) >= g->osc[0].size) { g->osc[0].p -= intToFrac(g->osc[0].size); if (!g->osc[0].loop) g->osc[0].halt = 1; if (g->osc[0].swap) { g->osc[0].halt = 1; g->osc[1].halt = 0; } } } if (!g->osc[1].halt) { s1 = g->osc[1].base[fracToInt(g->osc[1].p)]; g->osc[1].p += g->osc[1].pd; if ((uint)fracToInt(g->osc[1].p) >= g->osc[1].size) { g->osc[1].p -= intToFrac(g->osc[1].size); if (!g->osc[1].loop) g->osc[1].halt = 1; if (g->osc[1].swap) { g->osc[0].halt = 0; g->osc[1].halt = 1; } } } // Take envelope and MIDI volume information into account. // Also amplify. s0 *= vol * g->vel / 127 * 80 / 256; s1 *= vol * g->vel / 127 * 80 / 256; // Select output channel. if (g->osc[0].chn) outl += s0; else outr += s0; if (g->osc[1].chn) outl += s1; else outr += s1; } if (outl > 32768) outl = 32768; if (outl < -32767) outl = -32767; if (outr > 32768) outr = 32768; if (outr < -32767) outr = -32767; *p++ = outl; *p++ = outr; } return _outSize * 2; }
void Sprite::blit(const Sprite &from, const Common::Rect &area, int32 x, int32 y, bool transp) { // Sanity checks assert((x >= 0) && (y >= 0) && (x <= 0x7FFF) && (y <= 0x7FFF)); if (!exists() || !from.exists()) return; Common::Rect toArea = getArea(true); toArea.left = x; toArea.top = y; if (toArea.isEmpty()) return; Common::Rect fromArea = from.getArea(); fromArea.clip(area); fromArea.setWidth (MIN(fromArea.width() , toArea.width())); fromArea.setHeight(MIN(fromArea.height(), toArea.height())); if (fromArea.isEmpty() || !fromArea.isValidRect()) return; int32 w = fromArea.width(); int32 h = fromArea.height(); const int32 fromTop = fracToInt(fromArea.top * from._scaleInverse); const int32 fromLeft = fracToInt(fromArea.left * from._scaleInverse); const byte *src = (const byte *) from._surfaceTrueColor.getBasePtr(fromLeft, fromTop); byte *dst = ( byte *) _surfaceTrueColor.getBasePtr(x, y); const uint8 *srcT = from._transparencyMap + fromTop * from._surfaceTrueColor.w + fromLeft; uint8 *dstT = _transparencyMap + y * _surfaceTrueColor.w + x; frac_t posW = 0, posH = 0; while (h-- > 0) { posW = 0; const byte *srcRow = src; byte *dstRow = dst; const uint8 *srcRowT = srcT; uint8 *dstRowT = dstT; for (int32 j = 0; j < w; j++, dstRow += _surfaceTrueColor.bytesPerPixel, dstRowT++) { if (!transp || (*srcRowT == 0)) { // Ignore transparency or source is solid => copy memcpy(dstRow, srcRow, _surfaceTrueColor.bytesPerPixel); *dstRowT = *srcRowT; } else if (*srcRowT == 2) { // Half-transparent if (*dstRowT == 1) // But destination is transparent => propagate memcpy(dstRow, srcRow, _surfaceTrueColor.bytesPerPixel); else // Destination is solid => mix ImgConv.mixTrueColor(dstRow, srcRow); *dstRowT = *srcRowT; } // Advance source data posW += from._scaleInverse; while (posW >= ((frac_t) FRAC_ONE)) { srcRow += from._surfaceTrueColor.bytesPerPixel; srcRowT++; posW -= FRAC_ONE; } } dst += _surfaceTrueColor.pitch; dstT += _surfaceTrueColor.w; // Advance source data posH += from._scaleInverse; while (posH >= ((frac_t) FRAC_ONE)) { src += from._surfaceTrueColor.pitch; srcT += from._surfaceTrueColor.w; posH -= FRAC_ONE; } } }
void DrawBackgnd() { int i; // playfield counter PLAYFIELD *pPlay; // playfield pointer int prevX, prevY; // save interger part of position Common::Point ptWin; // window top left if (pCurBgnd == NULL) return; // no current background // scroll each background playfield for (i = 0; i < pCurBgnd->numPlayfields; i++) { // get pointer to correct playfield pPlay = pCurBgnd->fieldArray + i; // save integer part of position prevX = fracToInt(pPlay->fieldX); prevY = fracToInt(pPlay->fieldY); // update scrolling pPlay->fieldX += pPlay->fieldXvel; pPlay->fieldY += pPlay->fieldYvel; // convert fixed point window pos to a int ptWin.x = fracToInt(pPlay->fieldX); ptWin.y = fracToInt(pPlay->fieldY); // set the moved flag if the playfield has moved if (prevX != ptWin.x || prevY != ptWin.y) pPlay->bMoved = true; // sort the display list for this background - just in case somebody has changed object Z positions SortObjectList((OBJECT *)&pPlay->pDispList); // generate clipping rects for all objects that have moved etc. FindMovingObjects((OBJECT *)&pPlay->pDispList, &ptWin, &pPlay->rcClip, false, pPlay->bMoved); // clear playfield moved flag pPlay->bMoved = false; } // merge the clipping rectangles MergeClipRect(); // redraw all playfields within the clipping rectangles const RectList &clipRects = GetClipRects(); for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) { // clear the clip rectangle on the virtual screen // for each background playfield for (i = 0; i < pCurBgnd->numPlayfields; i++) { Common::Rect rcPlayClip; // clip rect for this playfield // get pointer to correct playfield pPlay = pCurBgnd->fieldArray + i; // convert fixed point window pos to a int ptWin.x = fracToInt(pPlay->fieldX); ptWin.y = fracToInt(pPlay->fieldY); if (IntersectRectangle(rcPlayClip, pPlay->rcClip, *r)) // redraw all objects within this clipping rect UpdateClipRect((OBJECT *)&pPlay->pDispList, &ptWin, &rcPlayClip); } } // transfer any new palettes to the video DAC PalettesToVideoDAC(); // update the screen within the clipping rectangles for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) { UpdateScreenRect(*r); } g_system->updateScreen(); // delete all the clipping rectangles ResetClipRect(); }
int32 Sprite::getDefaultX(bool unscaled) const { if (unscaled || (_scale == FRAC_ONE)) return _defaultX; return fracToInt(_defaultX * _scale); }
int32 Sprite::getFeetY(bool unscaled) const { if (unscaled || (_scale == FRAC_ONE)) return _feetY; return fracToInt(_feetY * _scale); }
int32 Sprite::getHeight(bool unscaled) const { if (unscaled || (_scale == FRAC_ONE)) return _surfacePaletted.h; return fracToInt(_surfacePaletted.h * _scale); }
int32 Sprite::getWidth(bool unscaled) const { if (unscaled || (_scale == FRAC_ONE)) return _surfacePaletted.w; return fracToInt(_surfacePaletted.w * _scale); }
bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const { #ifdef SCUMM_LITTLE_ENDIAN if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888 #else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 #endif glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565 glIntFormat = GL_RGB; glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGBA5551 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; #ifndef USE_GLES #ifdef SCUMM_LITTLE_ENDIAN } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_INT_8_8_8_8; return true; #endif } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 // GL_BGRA does not exist in every GLES implementation so should not be configured if // USE_GLES is set. glIntFormat = GL_RGB; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_1_5_5_5_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_4_4_4_4_REV; return true; #ifdef SCUMM_BIG_ENDIAN } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_INT_8_8_8_8_REV; return true; #endif } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565 glIntFormat = GL_RGB; glFormat = GL_BGR; glType = GL_UNSIGNED_SHORT_5_6_5; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_5_5_5_1; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444 glIntFormat = GL_RGBA; glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444 glIntFormat = GL_RGBA; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; #endif } else { return false; } } frac_t OpenGLGraphicsManager::getDesiredGameScreenAspect() const { const uint width = _currentState.gameWidth; const uint height = _currentState.gameHeight; if (_currentState.aspectRatioCorrection) { // In case we enable aspect ratio correction we force a 4/3 ratio. // But just for 320x200 and 640x400 games, since other games do not need // this. if ((width == 320 && height == 200) || (width == 640 && height == 400)) { return intToFrac(4) / 3; } } return intToFrac(width) / height; } void OpenGLGraphicsManager::recalculateDisplayArea() { if (!_gameScreen || _outputScreenHeight == 0) { return; } const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; const frac_t desiredAspect = getDesiredGameScreenAspect(); _displayWidth = _outputScreenWidth; _displayHeight = _outputScreenHeight; // Adjust one dimension for mantaining the aspect ratio. if (outputAspect < desiredAspect) { _displayHeight = intToFrac(_displayWidth) / desiredAspect; } else if (outputAspect > desiredAspect) { _displayWidth = fracToInt(_displayHeight * desiredAspect); } // We center the screen in the middle for now. _displayX = (_outputScreenWidth - _displayWidth ) / 2; _displayY = (_outputScreenHeight - _displayHeight) / 2; } void OpenGLGraphicsManager::updateCursorPalette() { if (!_cursor || !_cursor->hasPalette()) { return; } if (_cursorPaletteEnabled) { _cursor->setPalette(0, 256, _cursorPalette); } else { _cursor->setPalette(0, 256, _gamePalette); } // We remove all alpha bits from the palette entry of the color key. // This makes sure its properly handled as color key. const Graphics::PixelFormat &hardwareFormat = _cursor->getHardwareFormat(); const uint32 aMask = (0xFF >> hardwareFormat.aLoss) << hardwareFormat.aShift; if (hardwareFormat.bytesPerPixel == 2) { uint16 *palette = (uint16 *)_cursor->getPalette() + _cursorKeyColor; *palette &= ~aMask; } else if (hardwareFormat.bytesPerPixel == 4) { uint32 *palette = (uint32 *)_cursor->getPalette() + _cursorKeyColor; *palette &= ~aMask; } else { warning("OpenGLGraphicsManager::updateCursorPalette: Unsupported pixel depth %d", hardwareFormat.bytesPerPixel); } } void OpenGLGraphicsManager::recalculateCursorScaling() { if (!_cursor || !_gameScreen) { return; } // By default we use the unscaled versions. _cursorHotspotXScaled = _cursorHotspotX; _cursorHotspotYScaled = _cursorHotspotY; _cursorWidthScaled = _cursor->getWidth(); _cursorHeightScaled = _cursor->getHeight(); // In case scaling is actually enabled we will scale the cursor according // to the game screen. if (!_cursorDontScale) { const frac_t screenScaleFactorX = intToFrac(_displayWidth) / _gameScreen->getWidth(); const frac_t screenScaleFactorY = intToFrac(_displayHeight) / _gameScreen->getHeight(); _cursorHotspotXScaled = fracToInt(_cursorHotspotXScaled * screenScaleFactorX); _cursorWidthScaled = fracToInt(_cursorWidthScaled * screenScaleFactorX); _cursorHotspotYScaled = fracToInt(_cursorHotspotYScaled * screenScaleFactorY); _cursorHeightScaled = fracToInt(_cursorHeightScaled * screenScaleFactorY); } } #ifdef USE_OSD const Graphics::Font *OpenGLGraphicsManager::getFontOSD() { return FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont); } #endif void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const { const uint width = _outputScreenWidth; const uint height = _outputScreenHeight; // A line of a BMP image must have a size divisible by 4. // We calculate the padding bytes needed here. // Since we use a 3 byte per pixel mode, we can use width % 4 here, since // it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the // usual way of computing the padding bytes required). const uint linePaddingSize = width % 4; const uint lineSize = width * 3 + linePaddingSize; // Allocate memory for screenshot uint8 *pixels = new uint8[lineSize * height]; // Get pixel data from OpenGL buffer GLCALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels)); // BMP stores as BGR. Since we can't assume that GL_BGR is supported we // will swap the components from the RGB we read to BGR on our own. for (uint y = height; y-- > 0;) { uint8 *line = pixels + y * lineSize; for (uint x = width; x > 0; --x, line += 3) { SWAP(line[0], line[2]); } } // Open file Common::DumpFile out; out.open(filename); // Write BMP header out.writeByte('B'); out.writeByte('M'); out.writeUint32LE(height * lineSize + 54); out.writeUint32LE(0); out.writeUint32LE(54); out.writeUint32LE(40); out.writeUint32LE(width); out.writeUint32LE(height); out.writeUint16LE(1); out.writeUint16LE(24); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); // Write pixel data to BMP out.write(pixels, lineSize * height); // Free allocated memory delete[] pixels; } } // End of namespace OpenGL
void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { _outputScreenWidth = width; _outputScreenHeight = height; // Setup coordinates system. GLCALL(glViewport(0, 0, _outputScreenWidth, _outputScreenHeight)); GLCALL(glMatrixMode(GL_PROJECTION)); GLCALL(glLoadIdentity()); #ifdef USE_GLES GLCALL(glOrthof(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1)); #else GLCALL(glOrtho(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1)); #endif GLCALL(glMatrixMode(GL_MODELVIEW)); GLCALL(glLoadIdentity()); uint overlayWidth = width; uint overlayHeight = height; // WORKAROUND: We can only support surfaces up to the maximum supported // texture size. Thus, in case we encounter a physical size bigger than // this maximum texture size we will simply use an overlay as big as // possible and then scale it to the physical display size. This sounds // bad but actually all recent chips should support full HD resolution // anyway. Thus, it should not be a real issue for modern hardware. if ( overlayWidth > (uint)Texture::getMaximumTextureSize() || overlayHeight > (uint)Texture::getMaximumTextureSize()) { const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; if (outputAspect > (frac_t)FRAC_ONE) { overlayWidth = Texture::getMaximumTextureSize(); overlayHeight = intToFrac(overlayWidth) / outputAspect; } else { overlayHeight = Texture::getMaximumTextureSize(); overlayWidth = fracToInt(overlayHeight * outputAspect); } } // HACK: We limit the minimal overlay size to 256x200, which is the // minimum of the dimensions of the two resolutions 256x240 (NES) and // 320x200 (many DOS games use this). This hopefully assure that our // GUI has working layouts. overlayWidth = MAX<uint>(overlayWidth, 256); overlayHeight = MAX<uint>(overlayHeight, 200); if (!_overlay || _overlay->getFormat() != _defaultFormatAlpha) { delete _overlay; _overlay = nullptr; _overlay = createTexture(_defaultFormatAlpha); assert(_overlay); // We always filter the overlay with GL_LINEAR. This assures it's // readable in case it needs to be scaled and does not affect it // otherwise. _overlay->enableLinearFiltering(true); } _overlay->allocate(overlayWidth, overlayHeight); _overlay->fill(0); #ifdef USE_OSD if (!_osd || _osd->getFormat() != _defaultFormatAlpha) { delete _osd; _osd = nullptr; _osd = createTexture(_defaultFormatAlpha); assert(_osd); // We always filter the osd with GL_LINEAR. This assures it's // readable in case it needs to be scaled and does not affect it // otherwise. _osd->enableLinearFiltering(true); } _osd->allocate(_overlay->getWidth(), _overlay->getHeight()); _osd->fill(0); #endif // Re-setup the scaling for the screen and cursor recalculateDisplayArea(); recalculateCursorScaling(); // Something changed, so update the screen change ID. ++_screenChangeID; }
uint32 SoundGen2GS::mixSound() { int i, b; memset(_sndBuffer, 0, BUFFER_SIZE << 1); if (!_playing || _playingSound == -1) return BUFFER_SIZE; // Handle Apple IIGS sound mixing here // TODO: Implement playing both waves in an oscillator // TODO: Implement swap-mode in an oscillator for (uint midiChan = 0; midiChan < _midiChannels.size(); midiChan++) { for (uint gsChan = 0; gsChan < _midiChannels[midiChan]._gsChannels.size(); gsChan++) { IIgsChannelInfo &channel = _midiChannels[midiChan]._gsChannels[gsChan]; if (channel.playing()) { // Only mix in actively playing channels // Frequency multiplier was 1076.0 based on tests made with MESS 0.117. // Tests made with KEGS32 averaged the multiplier to around 1045. // So this is a guess but maybe it's 1046.5... i.e. C6's frequency? double hertz = C6_FREQ * pow(SEMITONE, fracToDouble(channel.note)); channel.posAdd = doubleToFrac(hertz / getRate()); channel.vol = doubleToFrac(fracToDouble(channel.envVol) * fracToDouble(channel.chanVol) / 127.0); double tempVol = fracToDouble(channel.vol)/127.0; for (i = 0; i < IIGS_BUFFER_SIZE; i++) { b = channel.relocatedSample[fracToInt(channel.pos)]; // TODO: Find out what volume/amplification setting is loud enough // but still doesn't clip when playing many channels on it. _sndBuffer[i] += (int16) (b * tempVol * 256/4); channel.pos += channel.posAdd; if (channel.pos >= intToFrac(channel.size)) { if (channel.loop) { // Don't divide by zero on zero length samples channel.pos %= intToFrac(channel.size + (channel.size == 0)); // Probably we should loop the envelope too channel.envSeg = 0; channel.envVol = channel.startEnvVol; } else { channel.pos = channel.chanVol = 0; channel.end = true; break; } } } if (channel.envSeg < ENVELOPE_SEGMENT_COUNT) { const IIgsEnvelopeSegment &seg = channel.ins->env.seg[channel.envSeg]; // I currently assume enveloping works with the same speed as the MIDI // (i.e. with 1/60ths of a second ticks). // TODO: Check if enveloping really works with the same speed as MIDI frac_t envVolDelta = doubleToFrac(seg.inc/256.0); if (intToFrac(seg.bp) >= channel.envVol) { channel.envVol += envVolDelta; if (channel.envVol >= intToFrac(seg.bp)) { channel.envVol = intToFrac(seg.bp); channel.envSeg += 1; } } else { channel.envVol -= envVolDelta; if (channel.envVol <= intToFrac(seg.bp)) { channel.envVol = intToFrac(seg.bp); channel.envSeg += 1; } } } } } } removeStoppedSounds(); return IIGS_BUFFER_SIZE; }