summaryrefslogtreecommitdiff
path: root/src/gl/gl3raster.cpp
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/gl/gl3raster.cpp
downloadlibrw-847962910f0bff071f3bf07c9abb87764fb6cac3.tar.gz
librw-847962910f0bff071f3bf07c9abb87764fb6cac3.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/gl/gl3raster.cpp')
-rw-r--r--src/gl/gl3raster.cpp1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/src/gl/gl3raster.cpp b/src/gl/gl3raster.cpp
new file mode 100644
index 0000000..b7c3941
--- /dev/null
+++ b/src/gl/gl3raster.cpp
@@ -0,0 +1,1045 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+#include "rwgl3impl.h"
+
+#define PLUGIN_ID ID_DRIVER
+
+namespace rw {
+namespace gl3 {
+
+int32 nativeRasterOffset;
+
+static uint32
+getLevelSize(Raster *raster, int32 level)
+{
+ int i;
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ int w = raster->originalWidth;
+ int h = raster->originalHeight;
+ int s = raster->originalStride;
+ int minDim = 1;
+
+#ifdef RW_OPENGL
+ switch(natras->internalFormat){
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ minDim = 4;
+ break;
+ }
+#endif
+
+ for(i = 0; i < level; i++){
+ if(w > minDim){
+ w /= 2;
+ s /= 2;
+ }
+ if(h > minDim)
+ h /= 2;
+ }
+
+ return s*h;
+}
+
+#ifdef RW_OPENGL
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 1;
+ natras->bpp = 4;
+ raster->depth = 32;
+ break;
+ case Raster::C888:
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+ raster->depth = 24;
+ break;
+ case Raster::C1555:
+ natras->internalFormat = GL_RGB5_A1;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
+ natras->hasAlpha = 1;
+ natras->bpp = 2;
+ raster->depth = 16;
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ if(gl3Caps.gles){
+ // glReadPixels only supports GL_RGBA
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->bpp = 4;
+ }
+
+ raster->stride = raster->width*natras->bpp;
+
+ if(raster->format & Raster::MIPMAP){
+ int w = raster->width;
+ int h = raster->height;
+ natras->numLevels = 1;
+ while(w != 1 || h != 1){
+ natras->numLevels++;
+ if(w > 1) w /= 2;
+ if(h > 1) h /= 2;
+ }
+ }
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+ if(natras->autogenMipmap)
+ natras->numLevels = 1;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ // TODO: allocate other levels...probably
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, natras->numLevels-1);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+ return raster;
+}
+
+static Raster*
+rasterCreateCameraTexture(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+
+ // TODO: figure out what the backbuffer is and use that as a default
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 1;
+ natras->bpp = 4;
+ break;
+ case Raster::C888:
+ default:
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+ break;
+ case Raster::C1555:
+ natras->internalFormat = GL_RGB5_A1;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
+ natras->hasAlpha = 1;
+ natras->bpp = 2;
+ break;
+ }
+
+ // i don't remember why this was once here...
+ if(gl3Caps.gles){
+ // glReadPixels only supports GL_RGBA
+// natras->internalFormat = GL_RGBA8;
+// natras->format = GL_RGBA;
+// natras->type = GL_UNSIGNED_BYTE;
+// natras->bpp = 4;
+ }
+
+ raster->stride = raster->width*natras->bpp;
+
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+
+
+ glGenFramebuffers(1, &natras->fbo);
+ bindFramebuffer(natras->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, natras->texid, 0);
+ bindFramebuffer(0);
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+static Raster*
+rasterCreateCamera(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ // TODO: set/check width, height, depth, format?
+
+ // used for locking right now
+ raster->format = Raster::C888;
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+
+ natras->autogenMipmap = 0;
+
+ natras->texid = 0;
+ natras->fbo = 0;
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+static Raster*
+rasterCreateZbuffer(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ if(gl3Caps.gles){
+ // have to use RBO on GLES!!
+ glGenRenderbuffers(1, &natras->texid);
+ glBindRenderbuffer(GL_RENDERBUFFER, natras->texid);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, raster->width, raster->height);
+ }else{
+ // TODO: set/check width, height, depth, format?
+ natras->internalFormat = GL_DEPTH_STENCIL;
+ natras->format = GL_DEPTH_STENCIL;
+ natras->type = GL_UNSIGNED_INT_24_8;
+
+ natras->autogenMipmap = 0;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+ }
+ natras->fbo = 0;
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+
+#endif
+
+
+void
+allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha)
+{
+#ifdef RW_OPENGL
+ assert(raster->type == Raster::TEXTURE);
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(dxt){
+ case 1:
+ if(hasAlpha){
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ natras->format = GL_RGBA;
+ }else{
+ natras->internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+ natras->format = GL_RGB;
+ }
+ // bogus, but stride*height should be the size of the image
+ // 4x4 in 8 bytes
+ raster->stride = raster->width/2;
+ break;
+ case 3:
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ natras->format = GL_RGBA;
+ // 4x4 in 16 bytes
+ raster->stride = raster->width;
+ break;
+ case 5:
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ natras->format = GL_RGBA;
+ // 4x4 in 16 bytes
+ raster->stride = raster->width;
+ break;
+ default:
+ assert(0 && "invalid DXT format");
+ }
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = hasAlpha;
+ natras->bpp = 2;
+ raster->depth = 16;
+
+ natras->isCompressed = 1;
+ if(raster->format & Raster::MIPMAP)
+ natras->numLevels = numLevels;
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+ if(natras->autogenMipmap)
+ natras->numLevels = 1;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ // TODO: allocate other levels...probably
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, natras->numLevels-1);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+
+ raster->originalStride = raster->stride;
+
+ if(gl3Caps.gles && needToReadBackTextures){
+ // Uh oh, need to keep a copy in cpu memory
+ int32 i;
+ int32 size = 0;
+ for(i = 0; i < natras->numLevels; i++)
+ size += getLevelSize(raster, i);
+ uint8 *data = (uint8*)rwNew(sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(natras->numLevels-1)+size,
+ MEMDUR_EVENT | ID_DRIVER);
+ RasterLevels *levels = (RasterLevels*)data;
+ data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(natras->numLevels-1);
+ levels->numlevels = natras->numLevels;
+ levels->format = 0; // not needed
+ for(i = 0; i < natras->numLevels; i++){
+ levels->levels[i].data = data;
+ levels->levels[i].size = getLevelSize(raster, i);
+ levels->levels[i].width = 0; // we don't need those here, maybe later...
+ levels->levels[i].height = 0;
+ data += levels->levels[i].size;
+ }
+ natras->backingStore = levels;
+ }
+
+ raster->flags &= ~Raster::DONTALLOCATE;
+#endif
+}
+
+/*
+{ 0, 0, 0 },
+{ 16, 4, GL_RGBA }, // 1555
+{ 16, 3, GL_RGB }, // 565
+{ 16, 4, GL_RGBA }, // 4444
+{ 0, 0, 0 }, // LUM8
+{ 32, 4, GL_RGBA }, // 8888
+{ 24, 3, GL_RGB }, // 888
+{ 16, 3, GL_RGB }, // D16
+{ 24, 3, GL_RGB }, // D24
+{ 32, 4, GL_RGBA }, // D32
+{ 16, 3, GL_RGB }, // 555
+
+0,
+GL_RGB5_A1,
+GL_RGB5,
+GL_RGBA4,
+0,
+GL_RGBA8,
+GL_RGB8,
+GL_RGB5,
+GL_RGB8,
+GL_RGBA8,
+GL_RGB5
+*/
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ natras->isCompressed = 0;
+ natras->hasAlpha = 0;
+ natras->numLevels = 1;
+
+ Raster *ret = raster;
+
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags |= Raster::DONTALLOCATE;
+ raster->stride = 0;
+ goto ret;
+ }
+ if(raster->flags & Raster::DONTALLOCATE)
+ goto ret;
+
+ switch(raster->type){
+#ifdef RW_OPENGL
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ ret = rasterCreateTexture(raster);
+ break;
+ case Raster::CAMERATEXTURE:
+ ret = rasterCreateCameraTexture(raster);
+ break;
+ case Raster::ZBUFFER:
+ ret = rasterCreateZbuffer(raster);
+ break;
+ case Raster::CAMERA:
+ ret = rasterCreateCamera(raster);
+ break;
+#endif
+
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ret:
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalStride = raster->stride;
+ raster->originalPixels = raster->pixels;
+ return ret;
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+#ifdef RW_OPENGL
+ Gl3Raster *natras GETGL3RASTEREXT(raster);
+ uint8 *px;
+ uint32 allocSz;
+ int i;
+
+ assert(raster->privateFlags == 0);
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ for(i = 0; i < level; i++){
+ if(raster->width > 1){
+ raster->width /= 2;
+ raster->stride /= 2;
+ }
+ if(raster->height > 1)
+ raster->height /= 2;
+ }
+
+ allocSz = getLevelSize(raster, level);
+ px = (uint8*)rwMalloc(allocSz, MEMDUR_EVENT | ID_DRIVER);
+ assert(raster->pixels == nil);
+ raster->pixels = px;
+
+ if(lockMode & Raster::LOCKREAD || !(lockMode & Raster::LOCKNOFETCH)){
+ if(natras->isCompressed){
+ if(natras->backingStore){
+ assert(level < natras->backingStore->numlevels);
+ assert(allocSz >= natras->backingStore->levels[level].size);
+ memcpy(px, natras->backingStore->levels[level].data, allocSz);
+ }else{
+ // GLES is losing here
+ uint32 prev = bindTexture(natras->texid);
+ glGetCompressedTexImage(GL_TEXTURE_2D, level, px);
+ bindTexture(prev);
+ }
+ }else if(gl3Caps.gles){
+ GLuint fbo;
+ glGenFramebuffers(1, &fbo);
+ bindFramebuffer(fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, natras->texid, 0);
+ GLenum e = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+assert(natras->format == GL_RGBA);
+ glReadPixels(0, 0, raster->width, raster->height, natras->format, natras->type, px);
+//e = glGetError(); printf("GL err4 %x (%x)\n", e, natras->format);
+ bindFramebuffer(0);
+ glDeleteFramebuffers(1, &fbo);
+ }else{
+ uint32 prev = bindTexture(natras->texid);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glGetTexImage(GL_TEXTURE_2D, level, natras->format, natras->type, px);
+ bindTexture(prev);
+ }
+ }
+
+ raster->privateFlags = lockMode;
+ break;
+
+ case Raster::CAMERA:
+ if(lockMode & Raster::PRIVATELOCK_WRITE)
+ assert(0 && "can't lock framebuffer for writing");
+ raster->width = glGlobals.presentWidth;
+ raster->height = glGlobals.presentHeight;
+ raster->stride = raster->width*natras->bpp;
+ assert(natras->bpp == 3);
+ allocSz = raster->height*raster->stride;
+ px = (uint8*)rwMalloc(allocSz, MEMDUR_EVENT | ID_DRIVER);
+ assert(raster->pixels == nil);
+ raster->pixels = px;
+ glReadBuffer(GL_BACK);
+ glReadPixels(0, 0, raster->width, raster->height, GL_RGB, GL_UNSIGNED_BYTE, px);
+
+ raster->privateFlags = lockMode;
+ break;
+
+ default:
+ assert(0 && "cannot lock this type of raster yet");
+ return nil;
+ }
+
+ return px;
+#else
+ return nil;
+#endif
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+#ifdef RW_OPENGL
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ assert(raster->pixels);
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ if(raster->privateFlags & Raster::LOCKWRITE){
+ uint32 prev = bindTexture(natras->texid);
+ if(natras->isCompressed){
+ glCompressedTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
+ raster->width, raster->height, 0,
+ getLevelSize(raster, level),
+ raster->pixels);
+ if(natras->backingStore){
+ assert(level < natras->backingStore->numlevels);
+ memcpy(natras->backingStore->levels[level].data, raster->pixels,
+ natras->backingStore->levels[level].size);
+ }
+ }else{
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, raster->pixels);
+ }
+ if(level == 0 && natras->autogenMipmap)
+ glGenerateMipmap(GL_TEXTURE_2D);
+ bindTexture(prev);
+ }
+ break;
+
+ case Raster::CAMERA:
+ // TODO: write?
+ break;
+ }
+
+ rwFree(raster->pixels);
+ raster->pixels = nil;
+#endif
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->stride = raster->originalStride;
+ raster->pixels = raster->originalPixels;
+ raster->privateFlags = 0;
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ return GETGL3RASTEREXT(raster)->numLevels;
+}
+
+// Almost the same as d3d9 and ps2 function
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat)
+{
+ int32 width, height, depth, format;
+
+ assert((type&0xF) == Raster::TEXTURE);
+
+// for(width = 1; width < img->width; width <<= 1);
+// for(height = 1; height < img->height; height <<= 1);
+ // Perhaps non-power-of-2 textures are acceptable?
+ width = img->width;
+ height = img->height;
+
+ depth = img->depth;
+
+ if(depth <= 8)
+ depth = 32;
+
+ switch(depth){
+ case 32:
+ if(img->hasAlpha())
+ format = Raster::C8888;
+ else{
+ format = Raster::C888;
+ depth = 24;
+ }
+ break;
+ case 24:
+ format = Raster::C888;
+ break;
+ case 16:
+ format = Raster::C1555;
+ break;
+
+ case 8:
+ case 4:
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ format |= type;
+
+ *pWidth = width;
+ *pHeight = height;
+ *pDepth = depth;
+ *pFormat = format;
+
+ return 1;
+}
+
+bool32
+rasterFromImage(Raster *raster, Image *image)
+{
+ if((raster->type&0xF) != Raster::TEXTURE)
+ return 0;
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+
+ // Unpalettize image if necessary but don't change original
+ Image *truecolimg = nil;
+ if(image->depth <= 8){
+ truecolimg = Image::create(image->width, image->height, image->depth);
+ truecolimg->pixels = image->pixels;
+ truecolimg->stride = image->stride;
+ truecolimg->palette = image->palette;
+ truecolimg->unpalettize();
+ image = truecolimg;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ int32 format = raster->format&0xF00;
+ assert(!natras->isCompressed);
+ switch(image->depth){
+ case 32:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_RGBA8888;
+ else if(format == Raster::C8888)
+ conv = conv_RGBA8888_from_RGBA8888;
+ else if(format == Raster::C888)
+ conv = conv_RGB888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 24:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_RGB888;
+ else if(format == Raster::C8888)
+ conv = conv_RGBA8888_from_RGB888;
+ else if(format == Raster::C888)
+ conv = conv_RGB888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 16:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_ARGB1555;
+ else if(format == Raster::C1555)
+ conv = conv_RGBA5551_from_ARGB1555;
+ else
+ goto err;
+ break;
+
+ case 8:
+ case 4:
+ default:
+ err:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ natras->hasAlpha = image->hasAlpha();
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ unlock = true;
+ }
+
+ uint8 *pixels = raster->pixels;
+ assert(pixels);
+ uint8 *imgpixels = image->pixels + (image->height-1)*image->stride;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(rasrow, imgrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels -= image->stride;
+ pixels += raster->stride;
+ }
+ if(unlock)
+ raster->unlock(0);
+
+ if(truecolimg)
+ truecolimg->destroy();
+
+ return 1;
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ int32 depth;
+ Image *image;
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKREAD);
+ unlock = true;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ if(natras->isCompressed){
+ // TODO
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+ switch(raster->format & 0xF00){
+ case Raster::C1555:
+ depth = 16;
+ conv = conv_ARGB1555_from_RGBA5551;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ conv = conv_RGBA8888_from_RGBA8888;
+ break;
+ case Raster::C888:
+ depth = 24;
+ conv = conv_RGB888_from_RGB888;
+ break;
+
+ default:
+ case Raster::C555:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ if(raster->format & Raster::PAL4 ||
+ raster->format & Raster::PAL8){
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ uint8 *in, *out;
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ uint8 *imgpixels = image->pixels + (image->height-1)*image->stride;
+ uint8 *pixels = raster->pixels;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(imgrow, rasrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels -= image->stride;
+ pixels += raster->stride;
+ }
+
+ if(unlock)
+ raster->unlock(0);
+
+ return image;
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset);
+ ras->texid = 0;
+ ras->fbo = 0;
+ ras->fboMate = nil;
+ ras->backingStore = nil;
+ return object;
+}
+
+void evictRaster(Raster *raster);
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ Raster *raster = (Raster*)object;
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, object, offset);
+#ifdef RW_OPENGL
+ evictRaster(raster);
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::CAMERATEXTURE:
+ if(natras->fboMate){
+ // Break apart from currently associated zbuffer
+ Gl3Raster *zras = GETGL3RASTEREXT(natras->fboMate);
+ zras->fboMate = nil;
+ natras->fboMate = nil;
+ }
+ glDeleteFramebuffers(1, &natras->fbo);
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::ZBUFFER:
+ if(natras->fboMate){
+ // Detatch from FBO we may be attached to
+ Gl3Raster *oldfb = GETGL3RASTEREXT(natras->fboMate);
+ if(oldfb->fbo){
+ bindFramebuffer(oldfb->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+ }
+ oldfb->fboMate = nil;
+ }
+ if(gl3Caps.gles)
+ glDeleteRenderbuffers(1, &natras->texid);
+ else
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::CAMERA:
+ if(natras->fboMate){
+ // Break apart from currently associated zbuffer
+ Gl3Raster *zras = GETGL3RASTEREXT(natras->fboMate);
+ zras->fboMate = nil;
+ natras->fboMate = nil;
+ }
+ break;
+ }
+ natras->texid = 0;
+ natras->fbo = 0;
+
+ free(natras->backingStore);
+
+#endif
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *, int32 offset, int32)
+{
+ Gl3Raster *d = PLUGINOFFSET(Gl3Raster, dst, offset);
+ d->texid = 0;
+ d->fbo = 0;
+ d->fboMate = nil;
+ d->backingStore = nil;
+ return dst;
+}
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_GL3){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ stream->read8(tex->name, 32);
+ stream->read8(tex->mask, 32);
+
+ // Raster
+ uint32 format = stream->readU32();
+ int32 width = stream->readI32();
+ int32 height = stream->readI32();
+ int32 depth = stream->readI32();
+ int32 numLevels = stream->readI32();
+
+ // Native raster
+ int32 subplatform = stream->readI32();
+ int32 flags = stream->readI32();
+ int32 compression = stream->readI32();
+
+ if(subplatform != gl3Caps.gles){
+ tex->destroy();
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+
+ Raster *raster;
+ Gl3Raster *natras;
+ if(flags & 2){
+ if(!gl3Caps.dxtSupported){
+ tex->destroy();
+ RWERROR((ERR_FORMAT_UNSUPPORTED));
+ return nil;
+ }
+ raster = Raster::create(width, height, depth, format | Raster::TEXTURE | Raster::DONTALLOCATE, PLATFORM_GL3);
+ allocateDXT(raster, compression, numLevels, flags & 1);
+ }else{
+ raster = Raster::create(width, height, depth, format | Raster::TEXTURE, PLATFORM_GL3);
+ }
+ assert(raster);
+ natras = GETGL3RASTEREXT(raster);
+ tex->raster = raster;
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = stream->readU32();
+ data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ stream->read8(data, size);
+ raster->unlock(i);
+ }
+ return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ Raster *raster = tex->raster;
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ int32 chunksize = getSizeNativeTexture(tex);
+ writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+ stream->writeU32(PLATFORM_GL3);
+
+ // Texture
+ stream->writeU32(tex->filterAddressing);
+ stream->write8(tex->name, 32);
+ stream->write8(tex->mask, 32);
+
+ // Raster
+ int32 numLevels = natras->numLevels;
+ stream->writeI32(raster->format);
+ stream->writeI32(raster->width);
+ stream->writeI32(raster->height);
+ stream->writeI32(raster->depth);
+ stream->writeI32(numLevels);
+
+ // Native raster
+ int32 flags = 0;
+ int32 compression = 0;
+ if(natras->hasAlpha)
+ flags |= 1;
+ if(natras->isCompressed){
+ flags |= 2;
+ switch(natras->internalFormat){
+#ifdef RW_OPENGL
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ compression = 1;
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ compression = 3;
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ compression = 5;
+ break;
+#endif
+ default:
+ assert(0 && "unknown compression");
+ }
+ }
+ stream->writeI32(gl3Caps.gles);
+ stream->writeI32(flags);
+ stream->writeI32(compression);
+ // TODO: auto mipmaps?
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = getLevelSize(raster, i);
+ stream->writeU32(size);
+ data = raster->lock(i, Raster::LOCKREAD);
+ stream->write8(data, size);
+ raster->unlock(i);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 72 + 32;
+ int32 levels = tex->raster->getNumLevels();
+ for(int32 i = 0; i < levels; i++)
+ size += 4 + getLevelSize(tex->raster, i);
+ return size;
+}
+
+
+
+void registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(Gl3Raster),
+ ID_RASTERGL3,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+}
+
+}
+}