TrustedUser
2,147
edits
m (Text replacement - "</source>" to "</syntaxhighlight>") |
|||
(15 intermediate revisions by 3 users not shown) | |||
Line 20: | Line 20: | ||
*Backends '''''must''''' place the highest color mode that is supported by the backend and the hardware at the beginning of the list returned by getSupportedFormats. | *Backends '''''must''''' place the highest color mode that is supported by the backend and the hardware at the beginning of the list returned by getSupportedFormats. | ||
*Backends ''should'' support graphics in RGB(A) color order, even if their hardware uses a different color order, but are not required to. | *Backends ''should'' support graphics in RGB(A) color order, even if their hardware uses a different color order, but are not required to. | ||
*Backends supporting color order conversion | *Backends supporting color order conversion with limited hardware may use Graphics::crossBlit, but are '''strongly recommended''' to use platform-optimized code. | ||
== Truecolor API initialization protocol == | == Truecolor API initialization protocol == | ||
Line 27: | Line 26: | ||
'''NOTE''': This API was designed with backwards-compatibility for 8-bit Graphics only engines in mind. If your engine does not make use of per-pixel RGB color graphics, you should not have to change anything, so long as ENABLE_RGB_COLOR is set in configuration during compilation, so that functions link properly. | '''NOTE''': This API was designed with backwards-compatibility for 8-bit Graphics only engines in mind. If your engine does not make use of per-pixel RGB color graphics, you should not have to change anything, so long as ENABLE_RGB_COLOR is set in configuration during compilation, so that functions link properly. | ||
# | #Init with desired pixel format | ||
#*If your engine can only produce graphics in one RGB color format, | #*If your engine can only produce graphics in one RGB color format, initialize a Graphics::PixelFormat to the desired format, and call initGraphics with a pointer to that format as the fourth parameter. | ||
#**For instance, if your engine can only produce graphics in RGB555, you | #**For instance, if your engine can only produce graphics in RGB555, you would say Graphics::PixelFormat myFormat(2, 3, 3, 3, 8, 10, 5, 0, 0); | ||
#*If your engine can easily support any RGB mode (for instance if it converts from YUV), call | #*If your engine can easily support any RGB mode (for instance if it converts from YUV), call initGraphics with NULL for the fourth parameter. | ||
#*If your engine can support more than one RGB mode, but not all of them... | #*If your engine can support more than one RGB mode, but not all of them... | ||
#*#Produce a Common::List of Graphics::PixelFormat objects describing the supported formats. This list must be in order of descending preference, so that the most desired format is first, and the least desired is last. | #*#Produce a Common::List of Graphics::PixelFormat objects describing the supported formats. This list must be in order of descending preference, so that the most desired format is first, and the least desired is last. | ||
#*# | #*#call initGraphics with this list of formats as the fourth parameter | ||
#Check the return value of OSystem::getScreenFormat() to see if setup of your desired format was successful. If setup was not successful, it will return Graphics::PixelFormat::createFormatCLUT8(); | #Check the return value of OSystem::getScreenFormat() to see if setup of your desired format was successful. If setup was not successful, it will return Graphics::PixelFormat::createFormatCLUT8(); | ||
#*If the setup was not successful, and your engine cannot run in 256 colors, display an error and return. | #*If the setup was not successful, and your engine cannot run in 256 colors, display an error and return. | ||
#*Otherwise, initialize your engine to use the pixel format that getScreenFormat returned, and run normally. | #*Otherwise, initialize your engine to use the pixel format that getScreenFormat returned, and run normally. | ||
==== Example ==== | |||
Here is an example of a simple engine that uses the best color depth available to display and color-cycle this gradient: [[Image:QuuxGradientRGB565.png]] | |||
<syntaxhighlight lang="cpp"> | |||
Common::Error QuuxEngine::run() { | |||
Graphics::PixelFormat ourFormat; | |||
// Request the backend to initialize a 640 x 480 surface with the best available format. | |||
initGraphics(640, 480, true, NULL); | |||
// If our engine could only handle one format, we would specify it here instead of asking the backend: | |||
// // RGB555 | |||
// ourFormat(2, 3, 3, 3, 8, 10, 5, 0, 0); | |||
// initGraphics(640, 480, true, &ourFormat); | |||
// If our engine could handle only a few formats, this would look quite different: | |||
// Common::List<Graphics::PixelFormat> ourFormatList; | |||
// | |||
// // RGB555 | |||
// ourFormat(2, 3, 3, 3, 8, 10, 5, 0, 0); | |||
// ourFormatList.push_back(ourFormat); | |||
// | |||
// // XRGB1555 | |||
// ourFormat(2, 3, 3, 3, 7, 10, 5, 0, 15); | |||
// ourFormatList.push_back(ourFormat); | |||
// | |||
// // Use the best format which is compatible between our engine and the backend | |||
// initGraphics(640, 480, true, ourFormatList); | |||
// Get the format the system was able to provide | |||
// in case it cannot support that format at our requested resolution | |||
ourFormat = _system->getScreenFormat(); | |||
byte *offscreenBuffer = (byte *)malloc(640 * 480 * ourFormat.bytesPerPixel); | |||
if (ourFormat.bytesPerPixel == 1) { | |||
// Initialize palette to simulate RGB332 | |||
// If our engine had no 256 color mode support, we would error out here: | |||
// return Common::kUnsupportedColorMode; | |||
byte palette[1024]; | |||
memset(&palette,0,1024); | |||
byte *dst = palette; | |||
for (byte r = 0; r < 8; r++) { | |||
for (byte g = 0; g < 8; g++) { | |||
for (byte b = 0; b < 4; b++) { | |||
dst[0] = r << 5; | |||
dst[1] = g << 5; | |||
dst[2] = b << 6; | |||
dst[3] = 0; | |||
dst += 4; | |||
} | |||
} | |||
} | |||
_system->setPalette(palette,0,256); | |||
} | |||
uint32 t = 0; | |||
// Create a mask to limit the color from exceeding the bitdepth | |||
// The result is equivalent to: | |||
// uint32 mask = 0; | |||
// for (int i = ourFormat.bytesPerPixel; i > 0; i--) { | |||
// mask <<= 8; | |||
// mask |= 0xFF; | |||
// } | |||
uint32 mask = (1 << (ourFormat.bytesPerPixel << 3)) - 1; | |||
// Repeat this until the event manager tells us to stop | |||
while (!shouldQuit()) { | |||
// Keep t from exceeding the number of bits in each pixel. | |||
// I think this is faster than "t %= (ourFormat.bytesPerPixel * 8);" would be. | |||
t &= (ourFormat.bytesPerPixel << 3) - 1; | |||
// Draw the actual gradient | |||
for (int16 y = 0; y < 480; y++) { | |||
uint8 *dst = offscreenBuffer + (y * 640 * ourFormat.bytesPerPixel); | |||
for (int16 x = 0; x < 640; x++) { | |||
uint32 color = (x * y) & mask; | |||
color = (color << t) | (color >> ((ourFormat.bytesPerPixel << 3) - t)); | |||
// Currently we have to jump through hoops to write variable-length data in an endian-safe manner. | |||
// In a real-life implementation, it would probably be better to have an if/else-if tree or | |||
// a switch to determine the correct WRITE_UINT* function to use in the current bitdepth. | |||
// Though, something like this might end up being necessary for 24-bit pixels, anyway. | |||
#ifdef SCUMM_BIG_ENDIAN | |||
for (int i = 0; i < ourFormat.bytesPerPixel; i++) { | |||
dst[ourFormat.bytesPerPixel - i] = color & 0xFF; | |||
color >>= 8; | |||
} | |||
dst += ourFormat.bytesPerPixel; | |||
#else | |||
for (int i = ourFormat.bytesPerPixel; i > 0; i--) { | |||
*dst++ = color & 0xFF; | |||
color >>= 8; | |||
} | |||
#endif | |||
} | |||
} | |||
// Copy our gradient to the screen. The pitch of our image is the width * the number of bytes per pixel. | |||
_system->copyRectToScreen(offscreenBuffer, 640 * ourFormat.bytesPerPixel, 0, 0, 640, 480); | |||
// Tell the system to update the screen. | |||
_system->updateScreen(); | |||
// Get new events from the event manager so the window doesn't appear non-responsive. | |||
parseEvents(); | |||
// Wait a semi-arbitrary length in order to animate fluidly, but not insanely fast | |||
_system->delayMillis(66); | |||
// Increment our time variable, which doubles as our bit-shift counter. | |||
t++; | |||
} | |||
return Common::kNoError; | |||
} | |||
</syntaxhighlight> | |||
=== Backend initialization protocol === | === Backend initialization protocol === | ||
Line 43: | Line 163: | ||
#*If format is NULL, use Graphics::PixelFormat::createFormatCLUT8() | #*If format is NULL, use Graphics::PixelFormat::createFormatCLUT8() | ||
#*If requested screen format is supported, attempt to set screen up with it. | #*If requested screen format is supported, attempt to set screen up with it. | ||
#**If setup is unsuccessful, fall back to | #**If setup is unsuccessful, fall back to previous color mode and set the value that getScreenFormat returns accordingly. | ||
#***Note: During game initialization, this '''must always''' result in a fall-back to 256 color mode with getScreenFormat returning a value equivalent to Graphics::PixelFormat::createFormatCLUT8. This may only have any other result if '''the same game''' has already run an initSize with a different format, and is trying to switch formats during runtime. | |||
#**If setup is successful, update the value that getScreenFormat returns to the value that was requested. | #**If setup is successful, update the value that getScreenFormat returns to the value that was requested. | ||
#***If format is supported by backend but not directly in hardware, ensure that graphics are converted in copyRectToScreen | #***If format is supported by backend but not directly in hardware, ensure that graphics are converted in copyRectToScreen | ||
#*If requested screen format is not supported, continue running in 256 color mode. | #*If requested screen format is not supported, continue running in 256 color mode. | ||
== Complete API | == Complete API reference == | ||
=== New functions === | === New functions === | ||
==== OSystem ==== | ==== OSystem ==== | ||
Line 59: | Line 180: | ||
**Backends which do not support fast conversion '''''must''''' put all modes directly supported in hardware, (and CLUT8), before modes that will require conversion during copyRectToScreen. | **Backends which do not support fast conversion '''''must''''' put all modes directly supported in hardware, (and CLUT8), before modes that will require conversion during copyRectToScreen. | ||
**Backends which support fast conversion ''should'' put larger colorspaces before smaller color spaces, but are not required to. | **Backends which support fast conversion ''should'' put larger colorspaces before smaller color spaces, but are not required to. | ||
==== Graphics::PixelFormat ==== | ==== Graphics::PixelFormat ==== | ||
*inline Graphics::PixelFormat Graphics::findCompatibleFormat(Common::List<Graphics::PixelFormat> backend, Common::List<Graphics::PixelFormat> frontend) | *inline Graphics::PixelFormat Graphics::findCompatibleFormat(Common::List<Graphics::PixelFormat> backend, Common::List<Graphics::PixelFormat> frontend) | ||
**Returns the first entry on the backend list that also occurs in the frontend list, or CLUT8 if there is no matching format. | **Returns the first entry on the backend list that also occurs in the frontend list, or CLUT8 if there is no matching format. | ||
*inline Graphics::PixelFormat (void) | |||
**creates an uninitialized PixelFormat. | |||
*inline Graphics::PixelFormat(byte BytesPerPixel, byte RBits, byte GBits, byte BBits, byte ABits, byte RShift, byte GShift, byte BShift, byte AShift) | |||
**creates an initialized PixelFormat. | |||
**[_]Bits is the width in bits of the relevant channel | |||
***RBits = red bits, GBits = green bits, BBits = blue bits, ABits = alpha bits. | |||
**[_]Shift is the number (starting from 0) of the least significant bit in the relevant channel, which is equal to the bitshift required to make a channel. | |||
***In RGB565, RShift is 11, GShift is 5, and BShift is 0. | |||
***In RGBA4444, RShift is 12, GShift is 8, BShift is 4, and AShift is 0. | |||
*static inline Graphics::PixelFormat Graphics::PixelFormat::createFormatCLUT8(void) | |||
**creates a PixelFormat set to indicate 256 color paletted mode | |||
**This method is provided for convenience, and is equivalent to initializing a Graphics::PixelFormat with the bytedepth of 1, component losses of 8, and component shifts of 0. | |||
***Which would be accomplished normally via Graphics::PixelFormat(1,8,8,8,8,0,0,0,0); | |||
**Because this methods are static, it can be called without creating a pixel format first | |||
***For instance, if (format == NULL) newFormat = Graphics::PixelFormat::createFormatCLUT8(); | |||
==== Miscellaneous ==== | ==== Miscellaneous ==== | ||
*bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, int w, int h, Graphics::PixelFormat dstFmt, Graphics::PixelFormat srcFmt) | *bool Graphics::crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, int w, int h, Graphics::PixelFormat dstFmt, Graphics::PixelFormat srcFmt) | ||
**blits a rectangle from a "surface" in srcFmt to a "surface" in dstFmt | **blits a rectangle from a "surface" in srcFmt to a "surface" in dstFmt | ||
**returns false if the blit fails (due to unsupported format conversion) | **returns false if the blit fails (due to unsupported format conversion) | ||
Line 78: | Line 209: | ||
=== Modified functions === | === Modified functions === | ||
====engine==== | ====engine==== | ||
*void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics::PixelFormat *format | *void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics::PixelFormat *format) | ||
** | **Now takes a format parameter, which is a pointer to a requested pixelformat | ||
**Uses | **Uses top item in backend's getSupportedFormats list if format is NULL | ||
**Now displays a warning if it recieves OSystem::kTransactionFormatNotSupported in return from endGFXTransaction | **Now displays a warning if it recieves OSystem::kTransactionFormatNotSupported in return from endGFXTransaction | ||
**Now overloaded to simplify initialization for the three engine types: | |||
*void initGraphics(int width, int height, bool defaultTo1xScaler) | |||
**A wrapper which sets format as a pointer to Graphics::PixelFormat::createFormatCLUT8(); | |||
*void initGraphics(int width, int height, bool defaultTo1xScaler, const Commmon::List<Graphics::PixelFormat> &formatList) | |||
**A wrapper which sets format as a pointer to the return value from Graphics::findCompatibleFormat(OSystem::getSupportedFormats(),formatList) | |||
====OSystem==== | ====OSystem==== | ||
Line 99: | Line 235: | ||
*Graphics::CursorManager::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8()) | *Graphics::CursorManager::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8()) | ||
**Can now take a format parameter, which is a Graphics::PixelFormat describing the pixel format of the cursor graphic, and defaults to 256 color mode. | **Can now take a format parameter, which is a Graphics::PixelFormat describing the pixel format of the cursor graphic, and defaults to 256 color mode. | ||
=== Modified Types === | |||
*enum Common::Error | |||
**Now includes a kUnsupportedColorMode value, for engines which get unsupported pixel formats after a format change request fails. | |||
*enum OSystem::TransactionError | |||
**Now includes a kTransactionFormatNotSupported value, for backends to announce failure to supply game screen with requested pixel format. |