From 433a2daa6a42b4cca3a715d4461a893be17ef61a Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Thu, 10 Mar 2016 21:49:42 -0500 Subject: GRAPHICS: Added ManagedSurface and Screen classes --- graphics/font.cpp | 11 ++ graphics/font.h | 3 + graphics/managed_surface.cpp | 258 +++++++++++++++++++++++++++++++ graphics/managed_surface.h | 355 +++++++++++++++++++++++++++++++++++++++++++ graphics/module.mk | 2 + graphics/screen.cpp | 129 ++++++++++++++++ graphics/screen.h | 111 ++++++++++++++ 7 files changed, 869 insertions(+) create mode 100644 graphics/managed_surface.cpp create mode 100644 graphics/managed_surface.h create mode 100644 graphics/screen.cpp create mode 100644 graphics/screen.h (limited to 'graphics') diff --git a/graphics/font.cpp b/graphics/font.cpp index dba48249bc..d709758948 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -21,6 +21,7 @@ */ #include "graphics/font.h" +#include "graphics/managed_surface.h" #include "common/array.h" #include "common/util.h" @@ -273,6 +274,16 @@ void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, drawStringImpl(*this, dst, str, x, y, w, color, align, 0); } +void Font::drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { + drawString(&dst->_innerSurface, str, x, y, w, color, align, deltax, useEllipsis); + dst->addDirtyRect(Common::Rect(x, y, x + w, y + getFontHeight())); +} + +void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align) const { + drawString(&dst->_innerSurface, str, x, y, w, color, align); + dst->addDirtyRect(Common::Rect(x, y, x + w, y + getFontHeight())); +} + int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const { return wordWrapTextImpl(*this, str, maxWidth, lines); } diff --git a/graphics/font.h b/graphics/font.h index 35f6792d7f..62e71a8568 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -34,6 +34,7 @@ template class Array; namespace Graphics { struct Surface; +class ManagedSurface; /** Text alignment modes */ enum TextAlign { @@ -145,6 +146,8 @@ public: // TODO: Add doxygen comments to this void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const; + void drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + void drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const; /** * Compute and return the width the string str has when rendered using this font. diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp new file mode 100644 index 0000000000..45db0bc52e --- /dev/null +++ b/graphics/managed_surface.cpp @@ -0,0 +1,258 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/managed_surface.h" +#include "common/algorithm.h" +#include "common/textconsole.h" + +namespace Graphics { + +const int SCALE_THRESHOLD = 0x100; + +ManagedSurface::ManagedSurface() : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _isManaged(false), _owner(nullptr) { +} + +ManagedSurface::ManagedSurface(const ManagedSurface &surf) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _isManaged(false), _owner(nullptr) { + *this = surf; +} + +ManagedSurface::ManagedSurface(int width, int height) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _isManaged(false), _owner(nullptr) { + create(width, height); +} + +ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _isManaged(false), _owner(nullptr) { + create(width, height, format); +} + +ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _isManaged(false), _owner(nullptr) { + create(surf, bounds); +} + +ManagedSurface::~ManagedSurface() { + free(); +} + +ManagedSurface &ManagedSurface::operator=(const ManagedSurface &surf) { + if (surf._isManaged) { + // Create a new surface and copy the pixels from the source surface + create(surf.w, surf.h, surf.format); + Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() + + surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels()); + } else { + // Source isn't managed, so simply copy it's fields + _isManaged = false; + _owner = surf._owner; + _offsetFromOwner = surf._offsetFromOwner; + + void *srcPixels = (void *)surf._innerSurface.getPixels(); + _innerSurface.setPixels(srcPixels); + _innerSurface.w = surf.w; + _innerSurface.h = surf.h; + _innerSurface.pitch = surf.pitch; + this->format = surf.format; + } + + return *this; +} + +void ManagedSurface::setPixels(void *newPixels) { + free(); + _innerSurface.setPixels(newPixels); +} + +void ManagedSurface::create(uint16 width, uint16 height) { + create(width, height, PixelFormat::createFormatCLUT8()); +} + +void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixelFormat) { + free(); + _innerSurface.create(width, height, pixelFormat); + + _isManaged = true; + markAllDirty(); +} + +void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) { + free(); + + _offsetFromOwner = Common::Point(bounds.left, bounds.top); + _innerSurface.setPixels(surf.getBasePtr(bounds.left, bounds.top)); + _innerSurface.pitch = surf.pitch; + _innerSurface.format = surf.format; + _innerSurface.w = bounds.width(); + _innerSurface.h = bounds.height(); + _owner = &surf; + _isManaged = false; +} + +void ManagedSurface::free() { + if (_isManaged) + _innerSurface.free(); + + _isManaged = false; + _owner = nullptr; + _offsetFromOwner = Common::Point(0, 0); +} + +bool ManagedSurface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) { + if (destBounds.left >= this->w || destBounds.top >= this->h || + destBounds.right <= 0 || destBounds.bottom <= 0) + return false; + + // Clip the bounds if necessary to fit on-screen + if (destBounds.right > this->w) { + srcBounds.right -= destBounds.right - this->w; + destBounds.right = this->w; + } + + if (destBounds.bottom > this->h) { + srcBounds.bottom -= destBounds.bottom - this->h; + destBounds.bottom = this->h; + } + + if (destBounds.top < 0) { + srcBounds.top += -destBounds.top; + destBounds.top = 0; + } + + if (destBounds.left < 0) { + srcBounds.left += -destBounds.left; + destBounds.left = 0; + } + + return true; +} + +void ManagedSurface::blitFrom(const Surface &src) { + blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0)); +} + +void ManagedSurface::blitFrom(const Surface &src, const Common::Point &destPos) { + blitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos); +} + +void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos) { + Common::Rect srcBounds = srcRect; + Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), + destPos.y + srcRect.height()); + assert(src.format.bytesPerPixel == format.bytesPerPixel); + + if (!srcRect.isValidRect() || !clip(srcBounds, destBounds)) + return; + + for (int y = 0; y < srcBounds.height(); ++y) { + const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y); + byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y); + Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP); + } + + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void ManagedSurface::transBlitFrom(const Surface &src, uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(0, 0, this->w, this->h), + transColor, false, overrideColor); +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Point &destPos, + uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(destPos.x, destPos.y, + destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos, uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, srcRect, Common::Rect(destPos.x, destPos.y, + destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); +} + +template +void transBlit(const Surface &src, const Common::Rect &srcRect, const Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { + int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width(); + int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height(); + + // Loop through drawing output lines + for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) { + if (destY < 0 || destY >= dest->h) + continue; + const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD); + T *destLine = (T *)dest->getBasePtr(destRect.left, destY); + + // Loop through drawing the pixels of the row + for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) { + if (destX < 0 || destX >= dest->w) + continue; + + T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; + if (srcVal != transColor) { + destLine[xCtr] = overrideColor ? overrideColor : srcVal; + } + } + } +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { + if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0) + return; + + if (format.bytesPerPixel == 1) + transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else if (format.bytesPerPixel == 2) + transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else if (format.bytesPerPixel == 4) + transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else + error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4"); + + // Mark the affected area + addDirtyRect(destRect); +} + +void ManagedSurface::markAllDirty() { + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void ManagedSurface::addDirtyRect(const Common::Rect &r) { + if (_owner) { + Common::Rect bounds = r; + bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y); + _owner->addDirtyRect(bounds); + } +} + +void ManagedSurface::clear(uint color) { + fillRect(getBounds(), color); +} + +} // End of namespace Graphics diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h new file mode 100644 index 0000000000..1d837fd8cd --- /dev/null +++ b/graphics/managed_surface.h @@ -0,0 +1,355 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GRAPHICS_MANAGED_SURFACE_H +#define GRAPHICS_MANAGED_SURFACE_H + +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "common/rect.h" + +namespace Graphics { + +class Font; + +/** + * A derived graphics surface, which handles automatically managing the allocated + * surface data block, as well as introducing several new blitting methods + */ +class ManagedSurface { + friend class Font; +private: + Surface _innerSurface; + bool _isManaged; + ManagedSurface *_owner; + Common::Point _offsetFromOwner; +protected: + /** + * Clips the given source bounds so the passed destBounds will be entirely on-screen + */ + bool clip(Common::Rect &srcBounds, Common::Rect &destBounds); + + /** + * Base method that descendent classes can override for recording affected + * dirty areas of the surface + */ + virtual void addDirtyRect(const Common::Rect &r); +public: + uint16 &w; + uint16 &h; + uint16 &pitch; + PixelFormat &format; +public: + /** + * Create the managed surface + */ + ManagedSurface(); + + /** + * Create a managed surface from another one + * Note that if the source has a managed surface, it will be duplicated + */ + ManagedSurface(const ManagedSurface &surf); + + /** + * Create the managed surface + */ + ManagedSurface(int width, int height); + + /** + * Create the managed surface + */ + ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat); + + /** + * Create the managed surface + */ + ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds); + + /** + * Destroy the managed surface + */ + virtual ~ManagedSurface(); + + /** + * Implements automatic conversion to a Graphics::Surface by + * simply returning the inner surface. This must be const, + * because we don't want changes being done directly to it, + * since it would bypass dirty rect handling + */ + operator const Surface &() const { return _innerSurface; } + const Surface &rawSurface() const { return _innerSurface; } + + /** + * Reassign one managed surface to another one + * Note that if the source has a managed surface, it will be duplicated + */ + ManagedSurface &operator=(const ManagedSurface &surf); + + /** + * Returns true if the surface has not yet been allocated + */ + bool empty() const { return w == 0 || h == 0 || _innerSurface.getPixels() == nullptr; } + + /** + * Returns true if the surface is managing it's own pixels + */ + bool isManaged() const { return _isManaged; } + + /** + * Return a pointer to the pixel at the specified point. + * + * @param x The x coordinate of the pixel. + * @param y The y coordinate of the pixel. + * @return Pointer to the pixel. + */ + inline const void *getBasePtr(int x, int y) const { + return _innerSurface.getBasePtr(x, y); + } + + /** + * Return a pointer to the pixel at the specified point. + * + * @param x The x coordinate of the pixel. + * @param y The y coordinate of the pixel. + * @return Pointer to the pixel. + */ + inline void *getBasePtr(int x, int y) { + return _innerSurface.getBasePtr(x, y); + } + + /** + * Get a reference to the pixel data + */ + inline void *getPixels() { return _innerSurface.getPixels(); } + inline const void *getPixels() const { return _innerSurface.getPixels(); } + + /** + * Sets the pixel data. + */ + virtual void setPixels(void *newPixels); + + /** + * Allocate memory for the pixel data of the surface. + */ + virtual void create(uint16 width, uint16 height); + + /** + * Allocate memory for the pixel data of the surface. + */ + virtual void create(uint16 width, uint16 height, const PixelFormat &pixelFormat); + + /** + * Sets up the surface as a sub-section of another passed parent surface. This surface + * will not own the pixels, and any dirty rect notifications will automatically be + * passed to the original parent surface. + * @remarks Note that this differs from Graphics::Surface::getSubArea, in that that + * method only adds a single initial dirty rect for the whole area, and then none further + */ + virtual void create(ManagedSurface &surf, const Common::Rect &bounds); + + /** + * Release the memory used by the pixels memory of this surface. This is the + * counterpart to create(). + */ + virtual void free(); + + /** + * Clears any pending dirty rects that have been generated for the surface + */ + virtual void clearDirtyRects() {} + + /** + * When the managed surface is a sub-section of a parent surface, returns the + * the offset in the parent surface that the surface starts at + */ + const Common::Point getOffsetFromOwner() const { return _offsetFromOwner; } + + /** + * Return a rect giving the bounds of the surface + */ + const Common::Rect getBounds() const { + return Common::Rect(0, 0, this->w, this->h); + } + + /** + * Copies another surface into this one + */ + void blitFrom(const Surface &src); + + /** + * Copies another surface into this one at a given destination position + */ + void blitFrom(const Surface &src, const Common::Point &destPos); + + /** + * Copies another surface into this one at a given destination position + */ + void blitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param destPos Destination position to draw the surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Point &destPos, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param srcRect Sub-section of source surface to draw + * @param destPos Destination position to draw the surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Point &destPos, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param srcRect Sub-section of source surface to draw + * @param destRect Destination area to draw the surface in. This can be sized differently + * then srcRect, allowing for arbitrary scaling of the image + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Rect &destRect, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Clear the entire surface + */ + void clear(uint color = 0); + + /** + * Mark the entire surface as dirty + */ + void markAllDirty(); + + /** + * Copies a bitmap to the Surface internal buffer. The pixel format + * of buffer must match the pixel format of the Surface. + */ + void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) { + _innerSurface.copyRectToSurface(buffer, srcPitch, destX, destY, width, height); + } + + /** + * Copies a bitmap to the Surface internal buffer. The pixel format + * of buffer must match the pixel format of the Surface. + */ + void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) { + _innerSurface.copyRectToSurface(srcSurface, destX, destY, subRect); + } + + /** + * Copy the data from another Surface, reinitializing the + * surface to match the dimensions of the passed surface + */ + void copyFrom(const ManagedSurface &surf) { + clearDirtyRects(); + _innerSurface.copyFrom(surf._innerSurface); + } + + /** + * Draw a line. + */ + void drawLine(int x0, int y0, int x1, int y1, uint32 color) { + _innerSurface.drawLine(x0, y0, x1, y1, color); + addDirtyRect(Common::Rect(x0, y0, x1, y1)); + } + + /** + * Draw a thick line. + */ + void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { + _innerSurface.drawThickLine(x0, y0, x1, y1, penX, penY, color); + addDirtyRect(Common::Rect(x0, y0, x1, y1)); + } + + /** + * Draw a horizontal line. + */ + void hLine(int x, int y, int x2, uint32 color) { + _innerSurface.hLine(x, y, x2, color); + addDirtyRect(Common::Rect(x, y, x2 + 1, y + 1)); + } + + /** + * Draw a vertical line. + */ + void vLine(int x, int y, int y2, uint32 color) { + _innerSurface.vLine(x, y, y2, color); + addDirtyRect(Common::Rect(x, y, x + 1, y2 + 1)); + } + + /** + * Fill a rect with a given color. + */ + void fillRect(Common::Rect r, uint32 color) { + _innerSurface.fillRect(r, color); + addDirtyRect(r); + } + + /** + * Draw a frame around a specified rect. + */ + void frameRect(const Common::Rect &r, uint32 color) { + _innerSurface.frameRect(r, color); + addDirtyRect(r); + } + + /** + * Returns a sub-area of the screen, but only adds a single initial dirty rect + * for the retrieved area. + */ + Surface getSubArea(const Common::Rect &area) { + addDirtyRect(area); + return _innerSurface.getSubArea(area); + } +}; + +} // End of namespace Graphics + + +#endif diff --git a/graphics/module.mk b/graphics/module.mk index b6919cf1ab..90f6a3199c 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -12,10 +12,12 @@ MODULE_OBJS := \ fonts/ttf.o \ fonts/winfont.o \ maccursor.o \ + managed_surface.o \ pixelformat.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ + screen.o \ sjis.o \ surface.o \ transform_struct.o \ diff --git a/graphics/screen.cpp b/graphics/screen.cpp new file mode 100644 index 0000000000..4169c98035 --- /dev/null +++ b/graphics/screen.cpp @@ -0,0 +1,129 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "common/algorithm.h" +#include "graphics/screen.h" +#include "graphics/palette.h" + +namespace Graphics { + +Screen::Screen(): ManagedSurface() { + create(g_system->getWidth(), g_system->getHeight()); +} + +Screen::Screen(int width, int height): ManagedSurface() { + create(width, height); +} + +Screen::Screen(int width, int height, PixelFormat pixelFormat): ManagedSurface() { + create(width, height, pixelFormat); +} + +void Screen::update() { + // Merge the dirty rects + mergeDirtyRects(); + + // Loop through copying dirty areas to the physical screen + Common::List::iterator i; + for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { + const Common::Rect &r = *i; + const byte *srcP = (const byte *)getBasePtr(r.left, r.top); + g_system->copyRectToScreen(srcP, pitch, r.left, r.top, + r.width(), r.height()); + } + + // Signal the physical screen to update + g_system->updateScreen(); + _dirtyRects.clear(); +} + + +void Screen::addDirtyRect(const Common::Rect &r) { + Common::Rect bounds = r; + bounds.clip(getBounds()); + bounds.translate(getOffsetFromOwner().x, getOffsetFromOwner().y); + + if (bounds.width() > 0 && bounds.height() > 0) + _dirtyRects.push_back(bounds); +} + +void Screen::makeAllDirty() { + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void Screen::mergeDirtyRects() { + Common::List::iterator rOuter, rInner; + + // Process the dirty rect list to find any rects to merge + for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { + rInner = rOuter; + while (++rInner != _dirtyRects.end()) { + + if ((*rOuter).intersects(*rInner)) { + // These two rectangles overlap, so merge them + unionRectangle(*rOuter, *rOuter, *rInner); + + // remove the inner rect from the list + _dirtyRects.erase(rInner); + + // move back to beginning of list + rInner = rOuter; + } + } + } +} + +bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) { + destRect = src1; + destRect.extend(src2); + + return !destRect.isEmpty(); +} + +void Screen::getPalette(byte palette[PALETTE_SIZE]) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->grabPalette(palette, 0, PALETTE_COUNT); +} + +void Screen::getPalette(byte *palette, uint start, uint num) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->grabPalette(palette, start, num); +} + +void Screen::setPalette(const byte palette[PALETTE_SIZE]) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->setPalette(palette, 0, PALETTE_COUNT); +} + +void Screen::setPalette(const byte *palette, uint start, uint num) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->setPalette(palette, start, num); +} + +void Screen::clearPalette() { + byte palette[PALETTE_SIZE]; + Common::fill(&palette[0], &palette[PALETTE_SIZE], 0); + setPalette(palette); +} + +} // End of namespace Graphics diff --git a/graphics/screen.h b/graphics/screen.h new file mode 100644 index 0000000000..3479ce847e --- /dev/null +++ b/graphics/screen.h @@ -0,0 +1,111 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GRAPHICS_SCREEN_H +#define GRAPHICS_SCREEN_H + +#include "graphics/managed_surface.h" +#include "graphics/pixelformat.h" +#include "common/list.h" +#include "common/rect.h" + +namespace Graphics { + +#define PALETTE_COUNT 256 +#define PALETTE_SIZE (256 * 3) + +class Screen : virtual public ManagedSurface { +private: + /** + * List of affected areas of the screen + */ + Common::List _dirtyRects; +private: + /** + * Merges together overlapping dirty areas of the screen + */ + void mergeDirtyRects(); + + /** + * Returns the union of two dirty area rectangles + */ + bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2); +protected: + /** + * Adds a rectangle to the list of modified areas of the screen during the + * current frame + */ + virtual void addDirtyRect(const Common::Rect &r); +public: + Screen(); + Screen(int width, int height); + Screen(int width, int height, PixelFormat pixelFormat); + + /** + * Returns true if there are any pending screen updates + */ + bool isDirty() const { return _dirtyRects.empty(); } + + /** + * Makes the whole screen dirty + */ + void makeAllDirty(); + + /** + * Clear the current dirty rects list + */ + virtual void clearDirtyRects() { _dirtyRects.clear(); } + + /** + * Updates the screen by copying any affected areas to the system + */ + virtual void update(); + + /** + * Return the currently active palette + */ + void getPalette(byte palette[PALETTE_SIZE]); + + /** + * Return a portion of the currently active palette + */ + void getPalette(byte *palette, uint start, uint num); + + /** + * Set the palette + */ + void setPalette(const byte palette[PALETTE_SIZE]); + + /** + * Set a subsection of the palette + */ + void setPalette(const byte *palette, uint start, uint num); + + /** + * Clears the current palette, setting all entries to black + */ + void clearPalette(); +}; + +} // End of namespace Graphics + +#endif -- cgit v1.2.3 From 390487aa43e5507d4d0fb4ae57e7cf726a94f4c1 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Thu, 17 Mar 2016 18:35:17 -0400 Subject: GRAPHICS: Cleanup of ManagedSurface and Screen classes --- graphics/managed_surface.cpp | 28 +++++++++++++++------------- graphics/managed_surface.h | 33 +++++++++++++++++++++++++++------ graphics/screen.h | 13 ++++++++++--- 3 files changed, 52 insertions(+), 22 deletions(-) (limited to 'graphics') diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp index 45db0bc52e..e493ab9f4e 100644 --- a/graphics/managed_surface.cpp +++ b/graphics/managed_surface.cpp @@ -30,30 +30,30 @@ const int SCALE_THRESHOLD = 0x100; ManagedSurface::ManagedSurface() : w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), - _isManaged(false), _owner(nullptr) { + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { } ManagedSurface::ManagedSurface(const ManagedSurface &surf) : w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), - _isManaged(false), _owner(nullptr) { + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { *this = surf; } ManagedSurface::ManagedSurface(int width, int height) : w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), - _isManaged(false), _owner(nullptr) { + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { create(width, height); } ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) : w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), - _isManaged(false), _owner(nullptr) { + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { create(width, height, format); } ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) : w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), - _isManaged(false), _owner(nullptr) { + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { create(surf, bounds); } @@ -62,17 +62,18 @@ ManagedSurface::~ManagedSurface() { } ManagedSurface &ManagedSurface::operator=(const ManagedSurface &surf) { - if (surf._isManaged) { + // Free any current surface + free(); + + if (surf._disposeAfterUse == DisposeAfterUse::YES) { // Create a new surface and copy the pixels from the source surface create(surf.w, surf.h, surf.format); Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() + surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels()); } else { - // Source isn't managed, so simply copy it's fields - _isManaged = false; + // Source isn't managed, so simply copy its fields _owner = surf._owner; _offsetFromOwner = surf._offsetFromOwner; - void *srcPixels = (void *)surf._innerSurface.getPixels(); _innerSurface.setPixels(srcPixels); _innerSurface.w = surf.w; @@ -97,7 +98,7 @@ void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixe free(); _innerSurface.create(width, height, pixelFormat); - _isManaged = true; + _disposeAfterUse = DisposeAfterUse::YES; markAllDirty(); } @@ -111,14 +112,14 @@ void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) { _innerSurface.w = bounds.width(); _innerSurface.h = bounds.height(); _owner = &surf; - _isManaged = false; + _disposeAfterUse = DisposeAfterUse::NO; } void ManagedSurface::free() { - if (_isManaged) + if (_disposeAfterUse == DisposeAfterUse::YES) _innerSurface.free(); - _isManaged = false; + _disposeAfterUse = DisposeAfterUse::NO; _owner = nullptr; _offsetFromOwner = Common::Point(0, 0); } @@ -246,6 +247,7 @@ void ManagedSurface::markAllDirty() { void ManagedSurface::addDirtyRect(const Common::Rect &r) { if (_owner) { Common::Rect bounds = r; + bounds.clip(Common::Rect(0, 0, this->w, this->h)); bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y); _owner->addDirtyRect(bounds); } diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h index 1d837fd8cd..bd0632a493 100644 --- a/graphics/managed_surface.h +++ b/graphics/managed_surface.h @@ -26,6 +26,7 @@ #include "graphics/pixelformat.h" #include "graphics/surface.h" #include "common/rect.h" +#include "common/types.h" namespace Graphics { @@ -38,9 +39,27 @@ class Font; class ManagedSurface { friend class Font; private: + /** + * The Graphics::Surface that the managed surface encapsulates + */ Surface _innerSurface; - bool _isManaged; + + /** + * If set, the inner surface will be freed when the surface is recreated, + * as well as when the surface is destroyed + */ + DisposeAfterUse::Flag _disposeAfterUse; + + /** + * Stores the owning surface if this If this managed surface represents + * a sub-section of another + */ ManagedSurface *_owner; + + /** + * For sub-section areas of an owning parent managed surface, this represents + * the offset from the parent's top-left corner this sub-surface starts at + */ Common::Point _offsetFromOwner; protected: /** @@ -65,8 +84,10 @@ public: ManagedSurface(); /** - * Create a managed surface from another one - * Note that if the source has a managed surface, it will be duplicated + * Create a managed surface from another one. + * If the source surface is maintaining it's own surface data, then + * this surface will create it's own surface of the same size and copy + * the contents from the source surface */ ManagedSurface(const ManagedSurface &surf); @@ -111,9 +132,9 @@ public: bool empty() const { return w == 0 || h == 0 || _innerSurface.getPixels() == nullptr; } /** - * Returns true if the surface is managing it's own pixels + * Returns true if the surface is managing its own pixels */ - bool isManaged() const { return _isManaged; } + DisposeAfterUse::Flag disposeAfterUse() const { return _disposeAfterUse; } /** * Return a pointer to the pixel at the specified point. @@ -304,7 +325,7 @@ public: */ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { _innerSurface.drawThickLine(x0, y0, x1, y1, penX, penY, color); - addDirtyRect(Common::Rect(x0, y0, x1, y1)); + addDirtyRect(Common::Rect(x0, y0, x1 + penX, y1 + penY)); } /** diff --git a/graphics/screen.h b/graphics/screen.h index 3479ce847e..29816120f1 100644 --- a/graphics/screen.h +++ b/graphics/screen.h @@ -33,6 +33,12 @@ namespace Graphics { #define PALETTE_COUNT 256 #define PALETTE_SIZE (256 * 3) +/** + * Implements a specialised surface that represents the screen. + * It keeps track of any areas of itself that are updated by drawing + * calls, and provides an update that method that blits the affected + * areas to the physical screen + */ class Screen : virtual public ManagedSurface { private: /** @@ -61,12 +67,13 @@ public: Screen(int width, int height, PixelFormat pixelFormat); /** - * Returns true if there are any pending screen updates + * Returns true if there are any pending screen updates (dirty areas) */ - bool isDirty() const { return _dirtyRects.empty(); } + bool isDirty() const { return !_dirtyRects.empty(); } /** - * Makes the whole screen dirty + * Marks the whole screen as dirty. This forces the next call to update + * to copy the entire screen contents */ void makeAllDirty(); -- cgit v1.2.3