gzdoom-gles/src/v_pfx.cpp

689 lines
16 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
/*
** v_pfx.cpp
** Pixel format conversion routines
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "doomtype.h"
#include "i_system.h"
#include "v_palette.h"
#include "v_pfx.h"
PfxUnion GPfxPal;
PfxState GPfx;
2016-03-01 15:47:10 +00:00
2017-03-08 17:47:52 +00:00
static bool AnalyzeMask (uint32_t mask, uint8_t *shift);
2016-03-01 15:47:10 +00:00
static void Palette16Generic (const PalEntry *pal);
static void Palette16R5G5B5 (const PalEntry *pal);
static void Palette16R5G6B5 (const PalEntry *pal);
static void Palette32Generic (const PalEntry *pal);
static void Palette32RGB (const PalEntry *pal);
static void Palette32BGR (const PalEntry *pal);
2017-03-08 17:47:52 +00:00
static void Scale8 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac);
2017-03-08 17:47:52 +00:00
static void Convert8 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac);
2017-03-08 17:47:52 +00:00
static void Convert16 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac);
2017-03-08 17:47:52 +00:00
static void Convert24 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac);
2017-03-08 17:47:52 +00:00
static void Convert32 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac);
void PfxState::SetFormat (int bits, uint32_t redMask, uint32_t greenMask, uint32_t blueMask)
2016-03-01 15:47:10 +00:00
{
switch (bits)
{
case -8:
Convert = Scale8;
SetPalette = NULL;
break;
case 8:
Convert = Convert8;
SetPalette = NULL;
break;
case 16:
if (redMask == 0x7c00 && greenMask == 0x03e0 && blueMask == 0x001f)
{
SetPalette = Palette16R5G5B5;
}
else if (redMask == 0xf800 && greenMask == 0x07e0 && blueMask == 0x001f)
{
SetPalette = Palette16R5G6B5;
}
else
{
SetPalette = Palette16Generic;
}
Convert = Convert16;
2017-03-08 17:47:52 +00:00
Masks.Bits16.Red = (uint16_t)redMask;
Masks.Bits16.Green = (uint16_t)greenMask;
Masks.Bits16.Blue = (uint16_t)blueMask;
2016-03-01 15:47:10 +00:00
break;
case 24:
if (redMask == 0xff0000 && greenMask == 0x00ff00 && blueMask == 0x0000ff)
{
SetPalette = Palette32RGB;
Convert = Convert24;
}
else if (redMask == 0x0000ff && greenMask == 0x00ff00 && blueMask == 0xff0000)
{
SetPalette = Palette32BGR;
Convert = Convert24;
}
else
{
I_FatalError ("24-bit displays are only supported if they are RGB or BGR");
};
break;
case 32:
if (redMask == 0xff0000 && greenMask == 0x00ff00 && blueMask == 0x0000ff)
{
SetPalette = Palette32RGB;
}
else if (redMask == 0x0000ff && greenMask == 0x00ff00 && blueMask == 0xff0000)
{
SetPalette = Palette32BGR;
}
else
{
SetPalette = Palette32Generic;
}
Convert = Convert32;
Masks.Bits32.Red = redMask;
Masks.Bits32.Green = greenMask;
Masks.Bits32.Blue = blueMask;
break;
default:
I_FatalError ("Can't draw to %d-bit displays", bits);
}
if (bits != 8 && bits != -8)
{
RedLeft = AnalyzeMask (redMask, &RedShift);
GreenLeft = AnalyzeMask (greenMask, &GreenShift);
BlueLeft = AnalyzeMask (blueMask, &BlueShift);
}
}
2017-03-08 17:47:52 +00:00
static bool AnalyzeMask (uint32_t mask, uint8_t *shiftout)
2016-03-01 15:47:10 +00:00
{
2017-03-08 17:47:52 +00:00
uint8_t shift = 0;
2016-03-01 15:47:10 +00:00
if (mask >= 0xff)
{
while (mask > 0xff)
{
shift++;
mask >>= 1;
}
*shiftout = shift;
return true;
}
else
{
while (mask < 0xff)
{
shift++;
mask <<= 1;
}
*shiftout = shift;
return false;
}
}
// Palette converters ------------------------------------------------------
static void Palette16Generic (const PalEntry *pal)
{
2017-03-08 17:47:52 +00:00
uint16_t *p16;
2016-03-01 15:47:10 +00:00
int i;
for (p16 = GPfxPal.Pal16, i = 256; i != 0; i--, pal++, p16++)
{
2017-03-08 17:47:52 +00:00
uint16_t rpart, gpart, bpart;
2016-03-01 15:47:10 +00:00
if (GPfx.RedLeft) rpart = pal->r << GPfx.RedShift;
else rpart = pal->r >> GPfx.RedShift;
if (GPfx.GreenLeft) gpart = pal->g << GPfx.GreenShift;
else gpart = pal->g >> GPfx.GreenShift;
if (GPfx.BlueLeft) bpart = pal->b << GPfx.BlueShift;
else bpart = pal->b >> GPfx.BlueShift;
*p16 = (rpart & GPfx.Masks.Bits16.Red) |
(gpart & GPfx.Masks.Bits16.Green) |
(bpart & GPfx.Masks.Bits16.Blue);
}
}
static void Palette16R5G5B5 (const PalEntry *pal)
{
2017-03-08 17:47:52 +00:00
uint16_t *p16;
2016-03-01 15:47:10 +00:00
int i;
for (p16 = GPfxPal.Pal16, i = 256; i != 0; i--, pal++, p16++)
{
*p16 = ((pal->r << 7) & 0x7c00) |
((pal->g << 2) & 0x03e0) |
((pal->b >> 3) & 0x001f);
}
}
static void Palette16R5G6B5 (const PalEntry *pal)
{
2017-03-08 17:47:52 +00:00
uint16_t *p16;
2016-03-01 15:47:10 +00:00
int i;
for (p16 = GPfxPal.Pal16, i = 256; i != 0; i--, pal++, p16++)
{
*p16 = ((pal->r << 8) & 0xf800) |
((pal->g << 3) & 0x07e0) |
((pal->b >> 3) & 0x001f);
}
}
static void Palette32Generic (const PalEntry *pal)
{
2017-03-08 17:47:52 +00:00
uint32_t *p32;
2016-03-01 15:47:10 +00:00
int i;
for (p32 = GPfxPal.Pal32, i = 256; i != 0; i--, pal++, p32++)
{
2017-03-08 17:47:52 +00:00
uint32_t rpart, gpart, bpart;
2016-03-01 15:47:10 +00:00
if (GPfx.RedLeft) rpart = pal->r << GPfx.RedShift;
else rpart = pal->r >> GPfx.RedShift;
if (GPfx.GreenLeft) gpart = pal->g << GPfx.GreenShift;
else gpart = pal->g >> GPfx.GreenShift;
if (GPfx.BlueLeft) bpart = pal->b << GPfx.BlueShift;
else bpart = pal->b >> GPfx.BlueShift;
*p32 = (rpart & GPfx.Masks.Bits32.Red) |
(gpart & GPfx.Masks.Bits32.Green) |
(bpart & GPfx.Masks.Bits32.Blue);
}
}
static void Palette32RGB (const PalEntry *pal)
{
memcpy (GPfxPal.Pal32, pal, 256*4);
}
static void Palette32BGR (const PalEntry *pal)
{
2017-03-08 17:47:52 +00:00
uint32_t *p32;
2016-03-01 15:47:10 +00:00
int i;
for (p32 = GPfxPal.Pal32, i = 256; i != 0; i--, pal++, p32++)
{
*p32 = (pal->r) | (pal->g << 8) | (pal->b << 16);
}
}
// Bitmap converters -------------------------------------------------------
2017-03-08 17:47:52 +00:00
static void Scale8 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac)
{
if ((destwidth | destheight) == 0)
{
return;
}
int x, y, savedx;
2017-03-08 17:47:52 +00:00
uint8_t *dest = (uint8_t *)destin;
2016-03-01 15:47:10 +00:00
if (xstep == FRACUNIT && ystep == FRACUNIT)
{
for (y = destheight; y != 0; y--)
{
memcpy(dest, src, destwidth);
dest += destpitch;
src += srcpitch;
}
}
else if (xstep == FRACUNIT/2 && ystep == FRACUNIT/2)
{
2017-03-08 17:47:52 +00:00
uint8_t *dest2 = dest + destpitch;
2016-03-01 15:47:10 +00:00
destpitch = destpitch * 2 - destwidth;
srcpitch -= destwidth / 2;
for (y = destheight / 2; y != 0; --y)
{
for (x = destwidth / 2; x != 0; --x)
{
2017-03-08 17:47:52 +00:00
uint8_t foo = src[0];
2016-03-01 15:47:10 +00:00
dest[0] = foo;
dest[1] = foo;
dest2[0] = foo;
dest2[1] = foo;
dest += 2;
dest2 += 2;
src += 1;
}
dest += destpitch;
dest2 += destpitch;
src += srcpitch;
}
}
else if (xstep == FRACUNIT/4 && ystep == FRACUNIT/4)
{
int gap = destpitch * 4 - destwidth;
srcpitch -= destwidth / 4;
for (y = destheight / 4; y != 0; --y)
{
2017-03-08 17:47:52 +00:00
for (uint8_t *end = dest + destpitch; dest != end; dest += 4)
2016-03-01 15:47:10 +00:00
{
2017-03-08 17:47:52 +00:00
uint8_t foo = src[0];
2016-03-01 15:47:10 +00:00
dest[0] = foo;
dest[1] = foo;
dest[2] = foo;
dest[3] = foo;
dest[0 + destpitch] = foo;
dest[1 + destpitch] = foo;
dest[2 + destpitch] = foo;
dest[3 + destpitch] = foo;
dest[0 + destpitch*2] = foo;
dest[1 + destpitch*2] = foo;
dest[2 + destpitch*2] = foo;
dest[3 + destpitch*2] = foo;
dest[0 + destpitch*3] = foo;
dest[1 + destpitch*3] = foo;
dest[2 + destpitch*3] = foo;
dest[3 + destpitch*3] = foo;
src += 1;
}
dest += gap;
src += srcpitch;
}
}
else
{
destpitch -= destwidth;
for (y = destheight; y != 0; y--)
{
fixed_t xf = xfrac;
x = destwidth;
while (((size_t)dest & 3) && x != 0)
{
*dest++ = src[xf >> FRACBITS];
xf += xstep;
x--;
}
for (savedx = x, x >>= 2; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
uint32_t work;
2016-03-01 15:47:10 +00:00
#ifdef __BIG_ENDIAN__
work = src[xf >> FRACBITS] << 24; xf += xstep;
work |= src[xf >> FRACBITS] << 16; xf += xstep;
work |= src[xf >> FRACBITS] << 8; xf += xstep;
work |= src[xf >> FRACBITS]; xf += xstep;
#else
work = src[xf >> FRACBITS]; xf += xstep;
work |= src[xf >> FRACBITS] << 8; xf += xstep;
work |= src[xf >> FRACBITS] << 16; xf += xstep;
work |= src[xf >> FRACBITS] << 24; xf += xstep;
#endif
2017-03-08 17:47:52 +00:00
*(uint32_t *)dest = work;
2016-03-01 15:47:10 +00:00
dest += 4;
}
for (savedx &= 3; savedx != 0; savedx--, xf += xstep)
{
*dest++ = src[xf >> FRACBITS];
}
yfrac += ystep;
while (yfrac >= FRACUNIT)
{
yfrac -= FRACUNIT;
src += srcpitch;
}
dest += destpitch;
}
}
}
2017-03-08 17:47:52 +00:00
static void Convert8 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac)
{
if ((destwidth | destheight) == 0)
{
return;
}
int x, y, savedx;
2017-03-08 17:47:52 +00:00
uint8_t *dest = (uint8_t *)destin;
2016-03-01 15:47:10 +00:00
destpitch -= destwidth;
if (xstep == FRACUNIT && ystep == FRACUNIT)
{
srcpitch -= destwidth;
for (y = destheight; y != 0; y--)
{
x = destwidth;
while (((size_t)dest & 3) && x != 0)
{
*dest++ = GPfxPal.Pal8[*src++];
x--;
}
for (savedx = x, x >>= 2; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
*(uint32_t *)dest =
2016-03-01 15:47:10 +00:00
#ifdef __BIG_ENDIAN__
(GPfxPal.Pal8[src[0]] << 24) |
(GPfxPal.Pal8[src[1]] << 16) |
(GPfxPal.Pal8[src[2]] << 8) |
(GPfxPal.Pal8[src[3]]);
#else
(GPfxPal.Pal8[src[0]]) |
(GPfxPal.Pal8[src[1]] << 8) |
(GPfxPal.Pal8[src[2]] << 16) |
(GPfxPal.Pal8[src[3]] << 24);
#endif
dest += 4;
src += 4;
}
for (savedx &= 3; savedx != 0; savedx--)
{
*dest++ = GPfxPal.Pal8[*src++];
}
dest += destpitch;
src += srcpitch;
}
}
else
{
for (y = destheight; y != 0; y--)
{
fixed_t xf = xfrac;
x = destwidth;
while (((size_t)dest & 3) && x != 0)
{
*dest++ = GPfxPal.Pal8[src[xf >> FRACBITS]];
xf += xstep;
x--;
}
for (savedx = x, x >>= 2; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
uint32_t work;
2016-03-01 15:47:10 +00:00
#ifdef __BIG_ENDIAN__
work = GPfxPal.Pal8[src[xf >> FRACBITS]] << 24; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]] << 16; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]] << 8; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]]; xf += xstep;
#else
work = GPfxPal.Pal8[src[xf >> FRACBITS]]; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]] << 8; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]] << 16; xf += xstep;
work |= GPfxPal.Pal8[src[xf >> FRACBITS]] << 24; xf += xstep;
#endif
2017-03-08 17:47:52 +00:00
*(uint32_t *)dest = work;
2016-03-01 15:47:10 +00:00
dest += 4;
}
for (savedx &= 3; savedx != 0; savedx--, xf += xstep)
{
*dest++ = GPfxPal.Pal8[src[xf >> FRACBITS]];
}
yfrac += ystep;
while (yfrac >= FRACUNIT)
{
yfrac -= FRACUNIT;
src += srcpitch;
}
dest += destpitch;
}
}
}
2017-03-08 17:47:52 +00:00
static void Convert16 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac)
{
if ((destwidth | destheight) == 0)
{
return;
}
int x, y, savedx;
2017-03-08 17:47:52 +00:00
uint16_t *dest = (uint16_t *)destin;
2016-03-01 15:47:10 +00:00
destpitch = (destpitch >> 1) - destwidth;
if (xstep == FRACUNIT && ystep == FRACUNIT)
{
srcpitch -= destwidth;
for (y = destheight; y != 0; y--)
{
x = destwidth;
if ((size_t)dest & 1)
{
x--;
*dest++ = GPfxPal.Pal16[*src++];
}
for (savedx = x, x >>= 1; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
*(uint32_t *)dest =
2016-03-01 15:47:10 +00:00
#ifdef __BIG_ENDIAN__
(GPfxPal.Pal16[src[0]] << 16) |
(GPfxPal.Pal16[src[1]]);
#else
(GPfxPal.Pal16[src[0]]) |
(GPfxPal.Pal16[src[1]] << 16);
#endif
dest += 2;
src += 2;
}
if (savedx & 1)
{
*dest++ = GPfxPal.Pal16[*src++];
}
dest += destpitch;
src += srcpitch;
}
}
else
{
for (y = destheight; y != 0; y--)
{
fixed_t xf = xfrac;
x = destwidth;
if ((size_t)dest & 1)
{
*dest++ = GPfxPal.Pal16[src[xf >> FRACBITS]];
xf += xstep;
x--;
}
for (savedx = x, x >>= 1; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
uint32_t work;
2016-03-01 15:47:10 +00:00
#ifdef __BIG_ENDIAN__
work = GPfxPal.Pal16[src[xf >> FRACBITS]] << 16; xf += xstep;
work |= GPfxPal.Pal16[src[xf >> FRACBITS]]; xf += xstep;
#else
work = GPfxPal.Pal16[src[xf >> FRACBITS]]; xf += xstep;
work |= GPfxPal.Pal16[src[xf >> FRACBITS]] << 16; xf += xstep;
#endif
2017-03-08 17:47:52 +00:00
*(uint32_t *)dest = work;
2016-03-01 15:47:10 +00:00
dest += 2;
}
if (savedx & 1)
{
*dest++ = GPfxPal.Pal16[src[xf >> FRACBITS]];
}
yfrac += ystep;
while (yfrac >= FRACUNIT)
{
yfrac -= FRACUNIT;
src += srcpitch;
}
dest += destpitch;
}
}
}
2017-03-08 17:47:52 +00:00
static void Convert24 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac)
{
if ((destwidth | destheight) == 0)
{
return;
}
int x, y;
2017-03-08 17:47:52 +00:00
uint8_t *dest = (uint8_t *)destin;
2016-03-01 15:47:10 +00:00
destpitch = destpitch - destwidth*3;
if (xstep == FRACUNIT && ystep == FRACUNIT)
{
srcpitch -= destwidth;
for (y = destheight; y != 0; y--)
{
for (x = destwidth; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
uint8_t *pe = GPfxPal.Pal24[src[0]];
2016-03-01 15:47:10 +00:00
dest[0] = pe[0];
dest[1] = pe[1];
dest[2] = pe[2];
dest += 3;
src++;
}
dest += destpitch;
src += srcpitch;
}
}
else
{
for (y = destheight; y != 0; y--)
{
fixed_t xf = xfrac;
for (x = destwidth; x != 0; x--)
{
2017-03-08 17:47:52 +00:00
uint8_t *pe = GPfxPal.Pal24[src[xf >> FRACBITS]];
2016-03-01 15:47:10 +00:00
dest[0] = pe[0];
dest[1] = pe[1];
dest[2] = pe[2];
xf += xstep;
dest += 2;
}
yfrac += ystep;
while (yfrac >= FRACUNIT)
{
yfrac -= FRACUNIT;
src += srcpitch;
}
dest += destpitch;
}
}
}
2017-03-08 17:47:52 +00:00
static void Convert32 (uint8_t *src, int srcpitch,
2016-03-01 15:47:10 +00:00
void *destin, int destpitch, int destwidth, int destheight,
fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac)
{
if ((destwidth | destheight) == 0)
{
return;
}
int x, y, savedx;
2017-03-08 17:47:52 +00:00
uint32_t *dest = (uint32_t *)destin;
2016-03-01 15:47:10 +00:00
destpitch = (destpitch >> 2) - destwidth;
if (xstep == FRACUNIT && ystep == FRACUNIT)
{
srcpitch -= destwidth;
for (y = destheight; y != 0; y--)
{
for (savedx = x = destwidth, x >>= 3; x != 0; x--)
{
dest[0] = GPfxPal.Pal32[src[0]];
dest[1] = GPfxPal.Pal32[src[1]];
dest[2] = GPfxPal.Pal32[src[2]];
dest[3] = GPfxPal.Pal32[src[3]];
dest[4] = GPfxPal.Pal32[src[4]];
dest[5] = GPfxPal.Pal32[src[5]];
dest[6] = GPfxPal.Pal32[src[6]];
dest[7] = GPfxPal.Pal32[src[7]];
dest += 8;
src += 8;
}
for (x = savedx & 7; x != 0; x--)
{
*dest++ = GPfxPal.Pal32[*src++];
}
dest += destpitch;
src += srcpitch;
}
}
else
{
for (y = destheight; y != 0; y--)
{
fixed_t xf = xfrac;
for (savedx = x = destwidth, x >>= 1; x != 0; x--)
{
dest[0] = GPfxPal.Pal32[src[xf >> FRACBITS]]; xf += xstep;
dest[1] = GPfxPal.Pal32[src[xf >> FRACBITS]]; xf += xstep;
dest += 2;
}
if (savedx & 1)
{
*dest++ = GPfxPal.Pal32[src[xf >> FRACBITS]];
}
yfrac += ystep;
while (yfrac >= FRACUNIT)
{
yfrac -= FRACUNIT;
src += srcpitch;
}
dest += destpitch;
}
}
}