mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-27 14:22:13 +00:00
- changed CreateTexBuffer to return its info in a structure and not as a naked pointer.
This commit is contained in:
parent
ab624c8c5a
commit
07f87e2542
8 changed files with 93 additions and 89 deletions
|
@ -226,16 +226,15 @@ void PostProcessShaderInstance::BindTextures()
|
||||||
if (it == mTextureHandles.end())
|
if (it == mTextureHandles.end())
|
||||||
{
|
{
|
||||||
// Why does this completely circumvent the normal way of handling textures?
|
// Why does this completely circumvent the normal way of handling textures?
|
||||||
int width, height;
|
// This absolutely needs fixing because it will also circumvent any potential caching system that may get implemented.
|
||||||
auto buffer = tex->CreateTexBuffer(0, width, height);
|
auto buffer = tex->CreateTexBuffer(0);
|
||||||
|
|
||||||
GLuint handle = 0;
|
GLuint handle = 0;
|
||||||
glGenTextures(1, &handle);
|
glGenTextures(1, &handle);
|
||||||
glBindTexture(GL_TEXTURE_2D, handle);
|
glBindTexture(GL_TEXTURE_2D, handle);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, buffer.mWidth, buffer.mHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer.mBuffer);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
delete[] buffer;
|
|
||||||
mTextureHandles[tex] = handle;
|
mTextureHandles[tex] = handle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -100,7 +100,7 @@ unsigned int FHardwareTexture::CreateTexture(unsigned char * buffer, int w, int
|
||||||
bool firstCall = glTex->glTexID == 0;
|
bool firstCall = glTex->glTexID == 0;
|
||||||
if (firstCall) glGenTextures(1,&glTex->glTexID);
|
if (firstCall) glGenTextures(1,&glTex->glTexID);
|
||||||
|
|
||||||
unsigned textureBinding = UINT_MAX;
|
int textureBinding = UINT_MAX;
|
||||||
if (texunit == -1) glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
if (texunit == -1) glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
||||||
if (texunit > 0) glActiveTexture(GL_TEXTURE0+texunit);
|
if (texunit > 0) glActiveTexture(GL_TEXTURE0+texunit);
|
||||||
if (texunit >= 0) lastbound[texunit] = glTex->glTexID;
|
if (texunit >= 0) lastbound[texunit] = glTex->glTexID;
|
||||||
|
@ -448,24 +448,25 @@ bool FHardwareTexture::BindOrCreate(FTexture *tex, int texunit, int clampmode, i
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
|
|
||||||
// Create this texture
|
// Create this texture
|
||||||
unsigned char * buffer = nullptr;
|
|
||||||
|
FTextureBuffer texbuffer;
|
||||||
|
|
||||||
if (!tex->isHardwareCanvas())
|
if (!tex->isHardwareCanvas())
|
||||||
{
|
{
|
||||||
buffer = tex->CreateTexBuffer(translation, w, h, flags | CTF_ProcessData);
|
texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
|
||||||
|
w = texbuffer.mWidth;
|
||||||
|
h = texbuffer.mHeight;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
w = tex->GetWidth();
|
w = tex->GetWidth();
|
||||||
h = tex->GetHeight();
|
h = tex->GetHeight();
|
||||||
}
|
}
|
||||||
if (!CreateTexture(buffer, w, h, texunit, needmipmap, translation, "FHardwareTexture.BindOrCreate"))
|
if (!CreateTexture(texbuffer.mBuffer, w, h, texunit, needmipmap, translation, "FHardwareTexture.BindOrCreate"))
|
||||||
{
|
{
|
||||||
// could not create texture
|
// could not create texture
|
||||||
delete[] buffer;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
delete[] buffer;
|
|
||||||
}
|
}
|
||||||
if (tex->isHardwareCanvas()) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
|
if (tex->isHardwareCanvas()) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
|
||||||
GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
|
GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
|
||||||
|
|
|
@ -341,19 +341,19 @@ void FMaterial::SetSpriteRect()
|
||||||
|
|
||||||
bool FMaterial::TrimBorders(uint16_t *rect)
|
bool FMaterial::TrimBorders(uint16_t *rect)
|
||||||
{
|
{
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
|
|
||||||
unsigned char *buffer = sourcetex->CreateTexBuffer(0, w, h);
|
auto texbuffer = sourcetex->CreateTexBuffer(0);
|
||||||
|
int w = texbuffer.mWidth;
|
||||||
|
int h = texbuffer.mHeight;
|
||||||
|
auto Buffer = texbuffer.mBuffer;
|
||||||
|
|
||||||
if (buffer == NULL)
|
if (texbuffer.mBuffer == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (w != mWidth || h != mHeight)
|
if (w != mWidth || h != mHeight)
|
||||||
{
|
{
|
||||||
// external Hires replacements cannot be trimmed.
|
// external Hires replacements cannot be trimmed.
|
||||||
delete [] buffer;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,14 +365,13 @@ bool FMaterial::TrimBorders(uint16_t *rect)
|
||||||
rect[1] = 0;
|
rect[1] = 0;
|
||||||
rect[2] = 1;
|
rect[2] = 1;
|
||||||
rect[3] = 1;
|
rect[3] = 1;
|
||||||
delete[] buffer;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int first, last;
|
int first, last;
|
||||||
|
|
||||||
for(first = 0; first < size; first++)
|
for(first = 0; first < size; first++)
|
||||||
{
|
{
|
||||||
if (buffer[first*4+3] != 0) break;
|
if (Buffer[first*4+3] != 0) break;
|
||||||
}
|
}
|
||||||
if (first >= size)
|
if (first >= size)
|
||||||
{
|
{
|
||||||
|
@ -381,13 +380,12 @@ bool FMaterial::TrimBorders(uint16_t *rect)
|
||||||
rect[1] = 0;
|
rect[1] = 0;
|
||||||
rect[2] = 1;
|
rect[2] = 1;
|
||||||
rect[3] = 1;
|
rect[3] = 1;
|
||||||
delete [] buffer;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(last = size-1; last >= first; last--)
|
for(last = size-1; last >= first; last--)
|
||||||
{
|
{
|
||||||
if (buffer[last*4+3] != 0) break;
|
if (Buffer[last*4+3] != 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect[1] = first / w;
|
rect[1] = first / w;
|
||||||
|
@ -396,7 +394,7 @@ bool FMaterial::TrimBorders(uint16_t *rect)
|
||||||
rect[0] = 0;
|
rect[0] = 0;
|
||||||
rect[2] = w;
|
rect[2] = w;
|
||||||
|
|
||||||
unsigned char *bufferoff = buffer + (rect[1] * w * 4);
|
unsigned char *bufferoff = Buffer + (rect[1] * w * 4);
|
||||||
h = rect[3];
|
h = rect[3];
|
||||||
|
|
||||||
for(int x = 0; x < w; x++)
|
for(int x = 0; x < w; x++)
|
||||||
|
@ -416,13 +414,11 @@ outl:
|
||||||
{
|
{
|
||||||
if (bufferoff[(x+y*w)*4+3] != 0)
|
if (bufferoff[(x+y*w)*4+3] != 0)
|
||||||
{
|
{
|
||||||
delete [] buffer;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rect[2]--;
|
rect[2]--;
|
||||||
}
|
}
|
||||||
delete [] buffer;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -656,12 +656,11 @@ bool I_SetCursor(FTexture *cursorpic)
|
||||||
{
|
{
|
||||||
// Create bitmap image representation
|
// Create bitmap image representation
|
||||||
|
|
||||||
int w, h;
|
auto sbuffer = cursorpic->CreateTexBuffer(0);
|
||||||
auto sbuffer = cursorpic->CreateTexBuffer(0, w, h);
|
|
||||||
|
|
||||||
const NSInteger imageWidth = w;
|
const NSInteger imageWidth = sbuffer.mWidth;
|
||||||
const NSInteger imageHeight = h;
|
const NSInteger imageHeight = sbuffer.mHeight;
|
||||||
const NSInteger imagePitch = w * 4;
|
const NSInteger imagePitch = sbuffer.mWidth * 4;
|
||||||
|
|
||||||
NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc];
|
NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc];
|
||||||
[bitmapImageRep initWithBitmapDataPlanes:NULL
|
[bitmapImageRep initWithBitmapDataPlanes:NULL
|
||||||
|
@ -678,8 +677,7 @@ bool I_SetCursor(FTexture *cursorpic)
|
||||||
// Load bitmap data to representation
|
// Load bitmap data to representation
|
||||||
|
|
||||||
uint8_t* buffer = [bitmapImageRep bitmapData];
|
uint8_t* buffer = [bitmapImageRep bitmapData];
|
||||||
memcpy(buffer, sbuffer, imagePitch * imageHeight);
|
memcpy(buffer, sbuffer.mBuffer, imagePitch * imageHeight);
|
||||||
delete [] sbuffer;
|
|
||||||
|
|
||||||
// Swap red and blue components in each pixel
|
// Swap red and blue components in each pixel
|
||||||
|
|
||||||
|
|
|
@ -397,7 +397,7 @@ bool FTexture::LoadHiresTexture(FTextureBuffer &texbuffer)
|
||||||
if (dwdata[i] == 0xffffff00 || dwdata[i] == 0xffff00ff) dwdata[i] = 0;
|
if (dwdata[i] == 0xffffff00 || dwdata[i] == 0xffff00ff) dwdata[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FContentId contentId;
|
FContentIdBuilder contentId;
|
||||||
contentId.id = 0;
|
contentId.id = 0;
|
||||||
contentId.imageID = HiresTexture->GetImage()->GetId();
|
contentId.imageID = HiresTexture->GetImage()->GetId();
|
||||||
texbuffer.mBuffer = buffer;
|
texbuffer.mBuffer = buffer;
|
||||||
|
|
|
@ -348,19 +348,19 @@ static void xbrzOldScale(size_t factor, const uint32_t* src, uint32_t* trg, int
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
// [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns
|
// [BB] Upsamples the texture in texbuffer.mBuffer, frees texbuffer.mBuffer and returns
|
||||||
// the upsampled buffer.
|
// the upsampled buffer.
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
void FTexture::CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha)
|
void FTexture::CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha)
|
||||||
{
|
{
|
||||||
// [BB] Make sure that outWidth and outHeight denote the size of
|
// [BB] Make sure that inWidth and inHeight denote the size of
|
||||||
// the returned buffer even if we don't upsample the input buffer.
|
// the returned buffer even if we don't upsample the input buffer.
|
||||||
int outWidth = texbuffer.mWidth;
|
int inWidth = texbuffer.mWidth;
|
||||||
int outHeight = texbuffer.mHeight;
|
int inHeight = texbuffer.mHeight;
|
||||||
|
|
||||||
// [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize.
|
// [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize.
|
||||||
if ( ( outWidth > gl_texture_hqresize_maxinputsize ) || ( outHeight > gl_texture_hqresize_maxinputsize ) )
|
if ((inWidth > gl_texture_hqresize_maxinputsize) || (inHeight > gl_texture_hqresize_maxinputsize))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// [BB] Don't try to upsample textures based off FCanvasTexture. (This should never get here in the first place!)
|
// [BB] Don't try to upsample textures based off FCanvasTexture. (This should never get here in the first place!)
|
||||||
|
@ -401,47 +401,53 @@ void FTexture::CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasA
|
||||||
if (mult < 2)
|
if (mult < 2)
|
||||||
type = 0;
|
type = 0;
|
||||||
|
|
||||||
switch (type)
|
|
||||||
|
if (type == 1)
|
||||||
{
|
{
|
||||||
case 1:
|
if (mult == 2)
|
||||||
switch(mult)
|
texbuffer.mBuffer = scaleNxHelper(&scale2x, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
{
|
else if (mult == 3)
|
||||||
case 2:
|
texbuffer.mBuffer = scaleNxHelper(&scale3x, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
return scaleNxHelper( &scale2x, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
else if (mult == 4)
|
||||||
case 3:
|
texbuffer.mBuffer = scaleNxHelper(&scale4x, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
return scaleNxHelper( &scale3x, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
else return;
|
||||||
default:
|
|
||||||
return scaleNxHelper( &scale4x, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
|
||||||
}
|
}
|
||||||
case 2:
|
else if (type == 2)
|
||||||
switch(mult)
|
|
||||||
{
|
{
|
||||||
case 2:
|
if (mult == 2)
|
||||||
return hqNxHelper( &hq2x_32, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxHelper(&hq2x_32, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
case 3:
|
else if (mult == 3)
|
||||||
return hqNxHelper( &hq3x_32, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxHelper(&hq3x_32, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
default:
|
else if (mult == 4)
|
||||||
return hqNxHelper( &hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxHelper(&hq4x_32, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
|
else return;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_MMX
|
#ifdef HAVE_MMX
|
||||||
case 3:
|
else if (type == 3)
|
||||||
switch(mult)
|
|
||||||
{
|
{
|
||||||
case 2:
|
if (mult == 2)
|
||||||
return hqNxAsmHelper( &HQnX_asm::hq2x_32, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq2x_32, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
case 3:
|
else if (mult == 3)
|
||||||
return hqNxAsmHelper( &HQnX_asm::hq3x_32, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq3x_32, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
default:
|
else if (mult == 4)
|
||||||
return hqNxAsmHelper( &HQnX_asm::hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq4x_32, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
|
else return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
case 4:
|
else if (type == 4)
|
||||||
return xbrzHelper(xbrz::scale, mult, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = xbrzHelper(xbrz::scale, mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
case 5:
|
else if (type == 5)
|
||||||
return xbrzHelper(xbrzOldScale, mult, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = xbrzHelper(xbrzOldScale, mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
case 6:
|
else if (type == 6)
|
||||||
return normalNxHelper( &normalNx, mult, inputBuffer, inWidth, inHeight, outWidth, outHeight );
|
texbuffer.mBuffer = normalNxHelper(&normalNx, mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Encode the scaling method in the content ID.
|
||||||
|
FContentIdBuilder contentId;
|
||||||
|
contentId.id = texbuffer.mContentId;
|
||||||
|
contentId.scaler = type;
|
||||||
|
contentId.scalefactor = mult;
|
||||||
|
texbuffer.mContentId = contentId.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return inputBuffer;
|
|
||||||
}
|
|
||||||
|
|
|
@ -671,7 +671,8 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
||||||
|
|
||||||
if (flags & CTF_CheckHires)
|
if (flags & CTF_CheckHires)
|
||||||
{
|
{
|
||||||
if (LoadHiresTexture(result)) return result;
|
// No image means that this cannot be checked,
|
||||||
|
if (GetImage() && LoadHiresTexture(result)) return result;
|
||||||
}
|
}
|
||||||
int exx = !!(flags & CTF_Expand);
|
int exx = !!(flags & CTF_Expand);
|
||||||
|
|
||||||
|
@ -699,18 +700,23 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
||||||
// A translated image is not conclusive for setting the texture's transparency info.
|
// A translated image is not conclusive for setting the texture's transparency info.
|
||||||
}
|
}
|
||||||
|
|
||||||
FContentId builder;
|
if (GetImage())
|
||||||
|
{
|
||||||
|
FContentIdBuilder builder;
|
||||||
builder.id = 0;
|
builder.id = 0;
|
||||||
builder.imageID = GetImage()->GetId();
|
builder.imageID = GetImage()->GetId();
|
||||||
builder.translation = MAX(0, translation);
|
builder.translation = MAX(0, translation);
|
||||||
builder.expand = exx;
|
builder.expand = exx;
|
||||||
|
result.mContentId = builder.id;
|
||||||
|
}
|
||||||
|
else result.mContentId = 0; // for non-image backed textures this has no meaning so leave it at 0.
|
||||||
|
|
||||||
result.mBuffer = buffer;
|
result.mBuffer = buffer;
|
||||||
result.mWidth = W;
|
result.mWidth = W;
|
||||||
result.mHeight = H;
|
result.mHeight = H;
|
||||||
result.mContentId = builder.id;
|
|
||||||
|
|
||||||
if (flags & CTF_ProcessData)
|
// Only do postprocessing for image-backed textures. (i.e. not for the burn texture which can also pass through here.)
|
||||||
|
if (GetImage() && flags & CTF_ProcessData)
|
||||||
{
|
{
|
||||||
CreateUpsampledTextureBuffer(result, !!isTransparent);
|
CreateUpsampledTextureBuffer(result, !!isTransparent);
|
||||||
ProcessData(result.mBuffer, result.mWidth, result.mHeight, false);
|
ProcessData(result.mBuffer, result.mWidth, result.mHeight, false);
|
||||||
|
@ -731,9 +737,8 @@ bool FTexture::GetTranslucency()
|
||||||
{
|
{
|
||||||
if (!bHasCanvas)
|
if (!bHasCanvas)
|
||||||
{
|
{
|
||||||
int w, h;
|
// This will calculate all we need, so just discard the result.
|
||||||
unsigned char *buffer = CreateTexBuffer(0, w, h);
|
CreateTexBuffer(0);
|
||||||
delete[] buffer;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include "colormatcher.h"
|
#include "colormatcher.h"
|
||||||
#include "r_data/renderstyle.h"
|
#include "r_data/renderstyle.h"
|
||||||
#include "r_data/r_translate.h"
|
#include "r_data/r_translate.h"
|
||||||
#include "hwrenderer/textures/hw_texmanager.h"
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// 15 because 0th texture is our texture
|
// 15 because 0th texture is our texture
|
||||||
|
@ -226,7 +225,7 @@ namespace OpenGLRenderer
|
||||||
class FHardwareTexture;
|
class FHardwareTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
union FContentId
|
union FContentIdBuilder
|
||||||
{
|
{
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
struct
|
struct
|
||||||
|
|
Loading…
Reference in a new issue