librw/src/gl/gl3raster.cpp

673 lines
15 KiB
C++
Raw Normal View History

2020-04-30 17:54:38 +02:00
#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"
#ifdef RW_OPENGL
#include <GL/glew.h>
#endif
#include "rwgl3.h"
#include "rwgl3shader.h"
#define PLUGIN_ID ID_DRIVER
namespace rw {
namespace gl3 {
int32 nativeRasterOffset;
#ifdef RW_OPENGL
static Raster*
2020-04-16 17:46:42 +02:00
rasterCreateTexture(Raster *raster)
{
2020-04-16 17:46:42 +02:00
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
switch(raster->format & 0xF00){
case Raster::C8888:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGBA8;
2020-04-16 17:46:42 +02:00
natras->format = GL_RGBA;
natras->type = GL_UNSIGNED_BYTE;
natras->hasAlpha = 1;
2020-05-13 20:46:27 +02:00
natras->bpp = 4;
2020-04-27 20:51:35 +02:00
raster->depth = 32;
break;
2020-04-16 17:46:42 +02:00
case Raster::C888:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGB8;
2020-04-16 17:46:42 +02:00
natras->format = GL_RGB;
natras->type = GL_UNSIGNED_BYTE;
natras->hasAlpha = 0;
2020-05-13 20:46:27 +02:00
natras->bpp = 3;
2020-04-27 20:51:35 +02:00
raster->depth = 24;
break;
2020-04-16 17:46:42 +02:00
case Raster::C1555:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGB5_A1;
2020-04-16 17:46:42 +02:00
natras->format = GL_RGBA;
2020-04-27 20:51:35 +02:00
natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
2020-04-16 17:46:42 +02:00
natras->hasAlpha = 1;
2020-05-13 20:46:27 +02:00
natras->bpp = 2;
2020-04-27 20:51:35 +02:00
raster->depth = 16;
break;
default:
RWERROR((ERR_INVRASTER));
return nil;
}
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
// glReadPixels only supports GL_RGBA
natras->internalFormat = GL_RGBA8;
natras->format = GL_RGBA;
natras->type = GL_UNSIGNED_BYTE;
natras->bpp = 4;
#endif
raster->stride = raster->width*natras->bpp;
2020-04-27 20:51:35 +02:00
2020-04-16 17:46:42 +02:00
glGenTextures(1, &natras->texid);
2020-08-10 13:35:50 +02:00
uint32 prev = bindTexture(natras->texid);
2020-04-16 17:46:42 +02:00
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;
2020-08-10 13:35:50 +02:00
bindTexture(prev);
return raster;
2020-04-16 17:46:42 +02:00
}
static Raster*
2020-04-16 17:46:42 +02:00
rasterCreateCameraTexture(Raster *raster)
{
if(raster->format & (Raster::PAL4 | Raster::PAL8)){
RWERROR((ERR_NOTEXTURE));
return nil;
}
2020-04-16 17:46:42 +02:00
// TODO: figure out what the backbuffer is and use that as a default
2016-07-15 11:55:52 +02:00
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
switch(raster->format & 0xF00){
case Raster::C8888:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGBA8;
2016-07-15 11:55:52 +02:00
natras->format = GL_RGBA;
natras->type = GL_UNSIGNED_BYTE;
2016-07-15 11:55:52 +02:00
natras->hasAlpha = 1;
2020-05-13 20:46:27 +02:00
natras->bpp = 4;
2016-07-15 11:55:52 +02:00
break;
case Raster::C888:
2020-04-26 21:51:33 +02:00
default:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGB8;
2016-07-15 11:55:52 +02:00
natras->format = GL_RGB;
natras->type = GL_UNSIGNED_BYTE;
2016-07-15 11:55:52 +02:00
natras->hasAlpha = 0;
2020-05-13 20:46:27 +02:00
natras->bpp = 3;
2016-07-15 11:55:52 +02:00
break;
case Raster::C1555:
2020-04-27 20:51:35 +02:00
natras->internalFormat = GL_RGB5_A1;
natras->format = GL_RGBA;
2020-04-27 20:51:35 +02:00
natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
natras->hasAlpha = 1;
2020-05-13 20:46:27 +02:00
natras->bpp = 2;
break;
2016-07-15 11:55:52 +02:00
}
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
// glReadPixels only supports GL_RGBA
// natras->internalFormat = GL_RGBA8;
// natras->format = GL_RGBA;
// natras->type = GL_UNSIGNED_BYTE;
// natras->bpp = 4;
#endif
raster->stride = raster->width*natras->bpp;
glGenTextures(1, &natras->texid);
2020-08-10 13:35:50 +02:00
uint32 prev = bindTexture(natras->texid);
2016-07-15 11:55:52 +02:00
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;
2020-08-10 13:35:50 +02:00
bindTexture(prev);
2020-08-10 13:31:33 +02:00
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;
2020-04-16 17:46:42 +02:00
}
static Raster*
2020-04-16 17:46:42 +02:00
rasterCreateCamera(Raster *raster)
{
2020-08-10 13:31:33 +02:00
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
2020-04-16 17:46:42 +02:00
// TODO: set/check width, height, depth, format?
raster->originalWidth = raster->width;
raster->originalHeight = raster->height;
raster->stride = 0;
raster->pixels = nil;
2020-08-10 13:31:33 +02:00
natras->texid = 0;
natras->fbo = 0;
natras->fboMate = nil;
return raster;
2020-04-16 17:46:42 +02:00
}
static Raster*
2020-04-16 17:46:42 +02:00
rasterCreateZbuffer(Raster *raster)
{
2020-08-10 13:31:33 +02:00
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
2020-04-16 17:46:42 +02:00
// TODO: set/check width, height, depth, format?
2020-08-10 13:31:33 +02:00
raster->originalWidth = raster->width;
raster->originalHeight = raster->height;
raster->stride = 0;
raster->pixels = nil;
natras->internalFormat = GL_DEPTH_COMPONENT;
natras->format = GL_DEPTH_COMPONENT;
natras->type = GL_UNSIGNED_BYTE;
glGenTextures(1, &natras->texid);
2020-08-10 13:35:50 +02:00
uint32 prev = bindTexture(natras->texid);
2020-08-10 13:31:33 +02:00
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;
2020-08-10 13:35:50 +02:00
bindTexture(prev);
2020-08-10 13:31:33 +02:00
natras->fbo = 0;
natras->fboMate = nil;
return raster;
2020-04-16 17:46:42 +02:00
}
#endif
2020-04-27 20:51:35 +02:00
/*
{ 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*
2020-04-16 17:46:42 +02:00
rasterCreate(Raster *raster)
{
2020-08-10 13:31:33 +02:00
if(raster->width == 0 || raster->height == 0){
raster->flags |= Raster::DONTALLOCATE;
raster->stride = 0;
return raster;
}
if(raster->flags & Raster::DONTALLOCATE)
return raster;
2020-04-16 17:46:42 +02:00
switch(raster->type){
#ifdef RW_OPENGL
2020-04-16 17:46:42 +02:00
case Raster::NORMAL:
case Raster::TEXTURE:
return rasterCreateTexture(raster);
2020-04-16 17:46:42 +02:00
case Raster::CAMERATEXTURE:
return rasterCreateCameraTexture(raster);
2020-04-16 17:46:42 +02:00
case Raster::ZBUFFER:
return rasterCreateZbuffer(raster);
2020-04-16 17:46:42 +02:00
case Raster::CAMERA:
return rasterCreateCamera(raster);
#endif
2020-04-16 17:46:42 +02:00
default:
RWERROR((ERR_INVRASTER));
return nil;
2020-04-16 17:46:42 +02:00
}
}
uint8*
2020-04-27 20:51:35 +02:00
rasterLock(Raster *raster, int32 level, int32 lockMode)
{
2020-04-27 20:51:35 +02:00
#ifdef RW_OPENGL
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
uint8 *px;
assert(raster->privateFlags == 0);
2020-04-28 12:44:28 +02:00
switch(raster->type & 0xF00){
case Raster::NORMAL:
case Raster::TEXTURE:
case Raster::CAMERATEXTURE:
px = (uint8*)rwMalloc(raster->stride*raster->height, MEMDUR_EVENT | ID_DRIVER);
2020-05-13 20:46:27 +02:00
memset(px, 0, raster->stride*raster->height);
2020-04-28 12:44:28 +02:00
assert(raster->pixels == nil);
raster->pixels = px;
if(lockMode & Raster::LOCKREAD || !(lockMode & Raster::LOCKNOFETCH)){
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
GLuint fbo;
GLenum e;
glGenFramebuffers(1, &fbo);
2020-08-10 13:31:33 +02:00
bindFramebuffer(fbo);
2020-05-13 20:46:27 +02:00
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, natras->texid, 0);
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);
2020-08-10 13:31:33 +02:00
bindFramebuffer(0);
2020-05-13 20:46:27 +02:00
glDeleteFramebuffers(1, &fbo);
#else
2020-04-28 12:44:28 +02:00
uint32 prev = bindTexture(natras->texid);
glGetTexImage(GL_TEXTURE_2D, level, natras->format, natras->type, px);
bindTexture(prev);
2020-05-13 20:46:27 +02:00
#endif
2020-04-28 12:44:28 +02:00
}
2020-04-27 20:51:35 +02:00
2020-04-28 12:44:28 +02:00
raster->privateFlags = lockMode;
break;
2020-04-27 20:51:35 +02:00
2020-04-28 12:44:28 +02:00
default:
assert(0 && "cannot lock this type of raster yet");
return nil;
}
2020-04-27 20:51:35 +02:00
return px;
#else
return nil;
2020-04-27 20:51:35 +02:00
#endif
}
void
2020-04-27 20:51:35 +02:00
rasterUnlock(Raster *raster, int32 level)
{
2020-04-27 20:51:35 +02:00
#ifdef RW_OPENGL
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
assert(raster->pixels);
if(raster->privateFlags & Raster::LOCKWRITE){
uint32 prev = bindTexture(natras->texid);
glTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
raster->width, raster->height,
0, natras->format, natras->type, raster->pixels);
bindTexture(prev);
}
rwFree(raster->pixels);
raster->pixels = nil;
raster->privateFlags = 0;
#endif
}
int32
rasterNumLevels(Raster*)
{
2020-04-27 20:51:35 +02:00
// TODO
return 1;
}
// 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);
2020-07-29 22:42:52 +02:00
// 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;
2020-04-27 20:51:35 +02:00
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;
2020-07-22 13:56:03 +02:00
truecolimg->unpalettize();
image = truecolimg;
}
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
2016-07-15 11:55:52 +02:00
switch(image->depth){
case 32:
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
2020-07-22 13:56:03 +02:00
conv = conv_RGBA8888_from_RGBA8888;
2020-05-13 20:46:27 +02:00
#else
2020-04-27 20:51:35 +02:00
if(raster->format == Raster::C8888)
2020-07-22 13:56:03 +02:00
conv = conv_RGBA8888_from_RGBA8888;
2020-04-27 20:51:35 +02:00
else if(raster->format == Raster::C888)
2020-07-22 13:56:03 +02:00
conv = conv_RGB888_from_RGB888;
2020-04-27 20:51:35 +02:00
else
goto err;
2020-05-13 20:46:27 +02:00
#endif
2016-07-15 11:55:52 +02:00
break;
case 24:
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
2020-08-01 08:16:14 +02:00
conv = conv_RGBA8888_from_RGB888;
2020-05-13 20:46:27 +02:00
#else
2020-05-12 21:31:44 +02:00
if(raster->format == Raster::C8888)
2020-07-22 13:56:03 +02:00
conv = conv_RGBA8888_from_RGB888;
2020-05-12 21:31:44 +02:00
else if(raster->format == Raster::C888)
2020-07-22 13:56:03 +02:00
conv = conv_RGB888_from_RGB888;
2020-04-27 20:51:35 +02:00
else
goto err;
2020-05-13 20:46:27 +02:00
#endif
2016-07-15 11:55:52 +02:00
break;
case 16:
2020-05-13 20:46:27 +02:00
#ifdef RW_GLES
2020-07-22 13:56:03 +02:00
conv = conv_RGBA8888_from_ARGB1555;
2020-05-13 20:46:27 +02:00
#else
2020-04-27 20:51:35 +02:00
if(raster->format == Raster::C1555)
2020-07-22 13:56:03 +02:00
conv = conv_RGBA5551_from_ARGB1555;
2020-04-27 20:51:35 +02:00
else
goto err;
2020-05-13 20:46:27 +02:00
#endif
break;
case 8:
case 4:
2016-07-15 11:55:52 +02:00
default:
err:
RWERROR((ERR_INVRASTER));
return 0;
2016-07-15 11:55:52 +02:00
}
natras->hasAlpha = image->hasAlpha();
2020-04-27 20:51:35 +02:00
uint8 *pixels = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
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;
2020-05-13 20:46:27 +02:00
rasrow += natras->bpp;
2020-04-27 20:51:35 +02:00
}
imgpixels -= image->stride;
pixels += raster->stride;
}
raster->unlock(0);
2020-07-29 22:42:52 +02:00
if(truecolimg)
truecolimg->destroy();
return 1;
}
static void*
createNativeRaster(void *object, int32 offset, int32)
{
Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset);
ras->texid = 0;
2020-08-10 13:31:33 +02:00
ras->fbo = 0;
ras->fboMate = nil;
return object;
}
static void*
2020-04-27 20:51:35 +02:00
destroyNativeRaster(void *object, int32 offset, int32)
{
2020-08-10 13:31:33 +02:00
Raster *raster = (Raster*)object;
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, object, offset);
2020-04-27 20:51:35 +02:00
#ifdef RW_OPENGL
2020-08-10 13:31:33 +02:00
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 = PLUGINOFFSET(Gl3Raster, natras->fboMate, offset);
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 = PLUGINOFFSET(Gl3Raster, natras->fboMate, nativeRasterOffset);
if(oldfb->fbo){
bindFramebuffer(oldfb->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
}
oldfb->fboMate = nil;
}
glDeleteTextures(1, &natras->texid);
break;
case Raster::CAMERA:
2020-10-24 22:50:52 +02:00
if(natras->fboMate){
// Break apart from currently associated zbuffer
Gl3Raster *zras = PLUGINOFFSET(Gl3Raster, natras->fboMate, offset);
zras->fboMate = nil;
natras->fboMate = nil;
}
2020-08-10 13:31:33 +02:00
break;
}
natras->texid = 0;
natras->fbo = 0;
2020-04-27 20:51:35 +02:00
#endif
return object;
}
static void*
copyNativeRaster(void *dst, void *, int32 offset, int32)
{
Gl3Raster *d = PLUGINOFFSET(Gl3Raster, dst, offset);
d->texid = 0;
2020-08-10 13:31:33 +02:00
d->fbo = 0;
d->fboMate = nil;
return dst;
}
2020-04-27 20:51:35 +02:00
static uint32
getLevelSize(Raster *raster, int32 level)
{
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
uint32 size = raster->stride*raster->height;
while(level--)
size /= 4;
return size;
}
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);
2020-04-27 20:51:35 +02:00
// Raster
uint32 format = stream->readU32();
int32 width = stream->readI32();
int32 height = stream->readI32();
int32 depth = stream->readI32();
int32 numLevels = stream->readI32();
Raster *raster;
Gl3Raster *natras;
raster = Raster::create(width, height, depth, format | Raster::TEXTURE, PLATFORM_GL3);
assert(raster);
natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
tex->raster = raster;
uint32 size;
uint8 *data;
for(int32 i = 0; i < numLevels; i++){
size = stream->readU32();
if(i < raster->getNumLevels()){
data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
stream->read8(data, size);
2020-04-27 20:51:35 +02:00
raster->unlock(i);
}else
stream->seek(size);
}
return tex;
}
void
writeNativeTexture(Texture *tex, Stream *stream)
{
Raster *raster = tex->raster;
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
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);
2020-04-27 20:51:35 +02:00
// Raster
int32 numLevels = raster->getNumLevels();
stream->writeI32(raster->format);
stream->writeI32(raster->width);
stream->writeI32(raster->height);
stream->writeI32(raster->depth);
stream->writeI32(numLevels);
// TODO: compression? 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);
2020-04-27 20:51:35 +02:00
raster->unlock(i);
}
}
uint32
getSizeNativeTexture(Texture *tex)
{
uint32 size = 12 + 72 + 20;
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);
}
}
}