diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp index c6f20cadbd..a86f93fc4d 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -33,6 +33,8 @@ // SCREEN WIPE PACKAGE // +EXTERN_CVAR(Bool, r_swtruecolor) + static int CurrentWipeType; static short *wipe_scr_start; @@ -77,10 +79,8 @@ bool wipe_initMelt (int ticks) { int i, r; -#ifdef PALETTEOUTPUT // copy start screen to main screen - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); -#endif + screen->DrawBlock(0, 0, SCREENWIDTH, SCREENHEIGHT, (canvas_pixel_t *)wipe_scr_start); // makes this wipe faster (in theory) // to have stuff in column-major format @@ -301,9 +301,6 @@ bool wipe_doBurn (int ticks) } else { -#ifndef PALETTEOUTPUT - // TO DO: RGB32k.All -#else int bglevel = 64-fglevel; DWORD *fg2rgb = Col2RGB8[fglevel]; DWORD *bg2rgb = Col2RGB8[bglevel]; @@ -311,7 +308,6 @@ bool wipe_doBurn (int ticks) DWORD bg = bg2rgb[fromold[x]]; fg = (fg+bg) | 0x1f07c1f; to[x] = RGB32k.All[fg & (fg>>15)]; -#endif done = false; } } @@ -342,9 +338,7 @@ bool wipe_doFade (int ticks) fade += ticks * 2; if (fade > 64) { -#ifdef PALETTEOUTPUT - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); -#endif + screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (canvas_pixel_t *)wipe_scr_end); return true; } else @@ -391,14 +385,15 @@ static bool (*wipes[])(int) = // Returns true if the wipe should be performed. bool wipe_StartScreen (int type) { + if (r_swtruecolor) + return false; + CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1); if (CurrentWipeType) { wipe_scr_start = new short[SCREENWIDTH * SCREENHEIGHT / 2]; -#ifdef PALETTEOUTPUT - screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); -#endif + screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (canvas_pixel_t *)wipe_scr_start); return true; } return false; @@ -406,13 +401,15 @@ bool wipe_StartScreen (int type) void wipe_EndScreen (void) { + if (r_swtruecolor) + return; + if (CurrentWipeType) { wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2]; -#ifdef PALETTEOUTPUT - screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); // restore start scr. -#endif + screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (canvas_pixel_t *)wipe_scr_end); + screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (canvas_pixel_t *)wipe_scr_start); // restore start scr. + // Initialize the wipe (*wipes[(CurrentWipeType-1)*3])(0); } @@ -423,6 +420,9 @@ bool wipe_ScreenWipe (int ticks) { bool rc; + if (r_swtruecolor) + return true; + if (CurrentWipeType == wipe_None) return true; @@ -436,6 +436,9 @@ bool wipe_ScreenWipe (int ticks) // Final things for the wipe void wipe_Cleanup() { + if (r_swtruecolor) + return; + if (wipe_scr_start != NULL) { delete[] wipe_scr_start; diff --git a/src/r_draw.cpp b/src/r_draw.cpp index 83a4472f39..cd34a71b4a 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -4601,3 +4601,30 @@ bool R_GetTransMaskDrawers (fixed_t (**tmvline1)(), void (**tmvline4)()) return false; } +void R_SetColorMapLight(BYTE *basecolormapdata, float light, int shade) +{ + if (r_swtruecolor) + { + dc_colormap = basecolormapdata; + dc_light = LIGHTSCALE(light, shade); + } + else + { + dc_colormap = basecolormapdata + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); + dc_light = 0; + } +} + +void R_SetDSColorMapLight(BYTE *basecolormapdata, float light, int shade) +{ + if (r_swtruecolor) + { + ds_colormap = basecolormapdata; + ds_light = LIGHTSCALE(light, shade); + } + else + { + ds_colormap = basecolormapdata + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); + ds_light = 0; + } +} diff --git a/src/r_draw.h b/src/r_draw.h index 17698c3609..db109dbee5 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -386,4 +386,10 @@ void maskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_ // transmaskwallscan is like maskwallscan, but it can also blend to the background void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int col)=R_GetColumn); +// Sets dc_colormap and dc_light to their appropriate values depending on the output format (pal vs true color) +void R_SetColorMapLight(BYTE *base_colormap, float light, int shade); + +// Same as R_SetColorMapLight, but for ds_colormap and ds_light +void R_SetDSColorMapLight(BYTE *base_colormap, float light, int shade); + #endif diff --git a/src/r_main.h b/src/r_main.h index 37a41a7631..c1034ea3eb 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -86,8 +86,6 @@ extern bool r_dontmaplines; // This is used instead of GETPALOOKUP when ds_colormap+dc_colormap is set to the base colormap #define LIGHTSCALE(vis,shade) ((shade)-FLOAT2FIXED(MIN(MAXLIGHTVIS,double(vis)))) -#ifndef PALETTEOUTPUT - // calculates the light constant passed to the shade_pal_index function inline uint32_t calc_light_multiplier(dsfixed_t light) { @@ -108,8 +106,6 @@ inline uint32_t shade_pal_index(uint32_t index, uint32_t light) return 0xff000000 | (red << 16) | (green << 8) | blue; } -#endif - extern double GlobVis; void R_SetVisibility(double visibility); diff --git a/src/r_plane.cpp b/src/r_plane.cpp index c8258a1ba0..8d0c882ba4 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -227,14 +227,7 @@ void R_MapPlane (int y, int x1) if (plane_shade) { // Determine lighting based on the span's distance from the viewer. -#ifndef PALETTEOUTPUT - ds_colormap = basecolormap->Maps; - ds_light = LIGHTSCALE(GlobVis * fabs(CenterY - y), planeshade); -#else - ds_colormap = basecolormap->Maps + (GETPALOOKUP ( - GlobVis * fabs(CenterY - y), planeshade) << COLORMAPSHIFT); - ds_light = 0; -#endif + R_SetDSColorMapLight(basecolormap->Maps, GlobVis * fabs(CenterY - y), planeshade); } #ifdef X86_ASM diff --git a/src/r_segs.cpp b/src/r_segs.cpp index fb27a99de7..548cd994f3 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -61,6 +61,8 @@ CVAR(Bool, r_np2, true, 0) //CVAR (Int, ty, 8, 0) //CVAR (Int, tx, 8, 0) +EXTERN_CVAR(Bool, r_swtruecolor) + #define HEIGHTBITS 12 #define HEIGHTSHIFT (FRACBITS-HEIGHTBITS) @@ -1138,13 +1140,7 @@ void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *l if (!fixed) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = basecolormapdata; - dc_light = LIGHTSCALE(light, wallshade); -#else - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(basecolormapdata, light, wallshade); } dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); @@ -1184,13 +1180,16 @@ void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *l for (z = 0; z < 4; ++z) { light += rw_lightstep; -#ifndef PALETTEOUTPUT - palookupoffse[z] = basecolormapdata; - palookuplight[z] = LIGHTSCALE(light, wallshade); -#else - palookupoffse[z] = basecolormapdata + (GETPALOOKUP(12/*light*/, wallshade) << COLORMAPSHIFT); - palookuplight[z] = 0; -#endif + if (r_swtruecolor) + { + palookupoffse[z] = basecolormapdata; + palookuplight[z] = LIGHTSCALE(light, wallshade); + } + else + { + palookupoffse[z] = basecolormapdata + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + palookuplight[z] = 0; + } } } @@ -1245,13 +1244,7 @@ void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *l if (!fixed) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = basecolormapdata; - dc_light = LIGHTSCALE(light, wallshade); -#else - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(basecolormapdata, light, wallshade); } dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); @@ -1690,13 +1683,7 @@ void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, f if (!fixed) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = basecolormapdata; - dc_light = LIGHTSCALE(light, wallshade); -#else - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(basecolormapdata, light, wallshade); } dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); @@ -1734,12 +1721,15 @@ void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, f for (z = 0; z < 4; ++z) { light += rw_lightstep; -#ifndef PALETTEOUTPUT - palookupoffse[z] = basecolormapdata; - palookuplight[z] = LIGHTSCALE(light, wallshade); -#else - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); -#endif + if (r_swtruecolor) + { + palookupoffse[z] = basecolormapdata; + palookuplight[z] = LIGHTSCALE(light, wallshade); + } + else + { + palookupoffse[z] = basecolormapdata + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + } } } @@ -1795,13 +1785,7 @@ void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, f if (!fixed) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = basecolormapdata; - dc_light = LIGHTSCALE(light, wallshade); -#else - dc_colormap = basecolormapdata + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(basecolormapdata, light, wallshade); } dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); @@ -3295,13 +3279,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, wallshade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, wallshade); } R_WallSpriteColumn (R_DrawMaskedColumn); dc_x++; @@ -3311,13 +3289,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, wallshade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, wallshade); } rt_initcols(nullptr); for (int zz = 4; zz; --zz) @@ -3332,13 +3304,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, wallshade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - dc_light = 0; -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, wallshade); } R_WallSpriteColumn (R_DrawMaskedColumn); dc_x++; diff --git a/src/r_things.cpp b/src/r_things.cpp index a6f6aea287..22538bd406 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -594,13 +594,7 @@ void R_DrawWallSprite(vissprite_t *spr) { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, shade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - dc_light = FLOAT2FIXED(MAXLIGHTVIS); -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, shade); } if (!R_ClipSpriteColumnWithPortals(spr)) R_WallSpriteColumn(R_DrawMaskedColumn); @@ -611,13 +605,7 @@ void R_DrawWallSprite(vissprite_t *spr) { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, shade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - dc_light = FLOAT2FIXED(MAXLIGHTVIS); -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, shade); } rt_initcols(nullptr); for (int zz = 4; zz; --zz) @@ -633,13 +621,7 @@ void R_DrawWallSprite(vissprite_t *spr) { if (calclighting) { // calculate lighting -#ifndef PALETTEOUTPUT - dc_colormap = usecolormap->Maps; - dc_light = LIGHTSCALE(rw_light, shade); -#else - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - dc_light = FLOAT2FIXED(MAXLIGHTVIS); -#endif + R_SetColorMapLight(usecolormap->Maps, rw_light, shade); } if (!R_ClipSpriteColumnWithPortals(spr)) R_WallSpriteColumn(R_DrawMaskedColumn); diff --git a/src/textures/canvastexture.cpp b/src/textures/canvastexture.cpp index 7388c13060..d1f70439f4 100644 --- a/src/textures/canvastexture.cpp +++ b/src/textures/canvastexture.cpp @@ -106,10 +106,7 @@ void FCanvasTexture::MakeTexture () Canvas = new DSimpleCanvas (Width, Height); Canvas->Lock (); GC::AddSoftRoot(Canvas); -#ifndef PALETTEOUTPUT - Pixels = new BYTE[Width*Height]; - bPixelsAllocated = true; -#else + if (Width != Height || Width != Canvas->GetPitch()) { Pixels = new BYTE[Width*Height]; @@ -117,10 +114,10 @@ void FCanvasTexture::MakeTexture () } else { - Pixels = Canvas->GetBuffer(); + Pixels = (BYTE*)Canvas->GetBuffer(); bPixelsAllocated = false; } -#endif + // Draw a special "unrendered" initial texture into the buffer. memset (Pixels, 0, Width*Height/2); memset (Pixels+Width*Height/2, 255, Width*Height/2); diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 8853fc9479..984375f255 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -77,6 +77,8 @@ extern "C" short spanend[MAXHEIGHT]; CVAR (Bool, hud_scale, false, CVAR_ARCHIVE); +EXTERN_CVAR(Bool, r_swtruecolor) + // For routines that take RGB colors, cache the previous lookup in case there // are several repetitions with the same color. static int LastPal = -1; @@ -1017,32 +1019,35 @@ void DCanvas::PUTTRANSDOT (int xx, int yy, int basecolor, int level) oldyyshifted = yy * GetPitch(); } -#ifndef PALETTEOUTPUT - canvas_pixel_t *spot = GetBuffer() + oldyyshifted + xx; + if (r_swtruecolor) + { + canvas_pixel_t *spot = GetBuffer() + oldyyshifted + xx; - uint32_t fg = shade_pal_index(basecolor, calc_light_multiplier(0)); - uint32_t fg_red = (fg >> 16) & 0xff; - uint32_t fg_green = (fg >> 8) & 0xff; - uint32_t fg_blue = fg & 0xff; + uint32_t fg = shade_pal_index(basecolor, calc_light_multiplier(0)); + uint32_t fg_red = (fg >> 16) & 0xff; + uint32_t fg_green = (fg >> 8) & 0xff; + uint32_t fg_blue = fg & 0xff; - uint32_t bg_red = (*spot >> 16) & 0xff; - uint32_t bg_green = (*spot >> 8) & 0xff; - uint32_t bg_blue = (*spot) & 0xff; + uint32_t bg_red = (*spot >> 16) & 0xff; + uint32_t bg_green = (*spot >> 8) & 0xff; + uint32_t bg_blue = (*spot) & 0xff; - uint32_t red = (fg_red + bg_red + 1) / 2; - uint32_t green = (fg_green + bg_green + 1) / 2; - uint32_t blue = (fg_blue + bg_blue + 1) / 2; + uint32_t red = (fg_red + bg_red + 1) / 2; + uint32_t green = (fg_green + bg_green + 1) / 2; + uint32_t blue = (fg_blue + bg_blue + 1) / 2; - *spot = 0xff000000 | (red << 16) | (green << 8) | blue; -#else - canvas_pixel_t *spot = GetBuffer() + oldyyshifted + xx; - DWORD *bg2rgb = Col2RGB8[1+level]; - DWORD *fg2rgb = Col2RGB8[63-level]; - DWORD fg = fg2rgb[basecolor]; - DWORD bg = bg2rgb[*spot]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; -#endif + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + } + else + { + canvas_pixel_t *spot = GetBuffer() + oldyyshifted + xx; + DWORD *bg2rgb = Col2RGB8[1+level]; + DWORD *fg2rgb = Col2RGB8[63-level]; + DWORD fg = fg2rgb[basecolor]; + DWORD bg = bg2rgb[*spot]; + bg = (fg+bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg>>15)]; + } } void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) diff --git a/src/v_video.cpp b/src/v_video.cpp index b6a626753d..2fb46e88ae 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -65,6 +65,7 @@ #include "menu/menu.h" #include "r_data/voxels.h" +EXTERN_CVAR(Bool, r_swtruecolor) FRenderer *Renderer; @@ -367,65 +368,68 @@ void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h) spot = Buffer + x1 + y1*Pitch; gap = Pitch - w; -#ifndef PALETTEOUTPUT - uint32_t fg = color.d; - uint32_t fg_red = (fg >> 16) & 0xff; - uint32_t fg_green = (fg >> 8) & 0xff; - uint32_t fg_blue = fg & 0xff; - - uint32_t alpha = (uint32_t)clamp(damount * 256 + 0.5f, 0.0f, 256.0f); - uint32_t inv_alpha = 256 - alpha; - - fg_red *= alpha; - fg_green *= alpha; - fg_blue *= alpha; - - for (y = h; y != 0; y--) + if (r_swtruecolor) { - for (x = w; x != 0; x--) + uint32_t fg = color.d; + uint32_t fg_red = (fg >> 16) & 0xff; + uint32_t fg_green = (fg >> 8) & 0xff; + uint32_t fg_blue = fg & 0xff; + + uint32_t alpha = (uint32_t)clamp(damount * 256 + 0.5f, 0.0f, 256.0f); + uint32_t inv_alpha = 256 - alpha; + + fg_red *= alpha; + fg_green *= alpha; + fg_blue *= alpha; + + for (y = h; y != 0; y--) { - uint32_t bg_red = (*spot >> 16) & 0xff; - uint32_t bg_green = (*spot >> 8) & 0xff; - uint32_t bg_blue = (*spot) & 0xff; + for (x = w; x != 0; x--) + { + uint32_t bg_red = (*spot >> 16) & 0xff; + uint32_t bg_green = (*spot >> 8) & 0xff; + uint32_t bg_blue = (*spot) & 0xff; - uint32_t red = (fg_red + bg_red * inv_alpha) / 256; - uint32_t green = (fg_green + bg_green * inv_alpha) / 256; - uint32_t blue = (fg_blue + bg_blue * inv_alpha) / 256; + uint32_t red = (fg_red + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue + bg_blue * inv_alpha) / 256; - *spot = 0xff000000 | (red << 16) | (green << 8) | blue; - spot++; + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + spot++; + } + spot += gap; } - spot += gap; } -#else - DWORD *bg2rgb; - DWORD fg; - + else { - int amount; + DWORD *bg2rgb; + DWORD fg; - amount = (int)(damount * 64); - bg2rgb = Col2RGB8[64-amount]; - - fg = (((color.r * amount) >> 4) << 20) | - ((color.g * amount) >> 4) | - (((color.b * amount) >> 4) << 10); - } - - for (y = h; y != 0; y--) - { - for (x = w; x != 0; x--) { - DWORD bg; + int amount; - bg = bg2rgb[(*spot)&0xff]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; - spot++; + amount = (int)(damount * 64); + bg2rgb = Col2RGB8[64-amount]; + + fg = (((color.r * amount) >> 4) << 20) | + ((color.g * amount) >> 4) | + (((color.b * amount) >> 4) << 10); + } + + for (y = h; y != 0; y--) + { + for (x = w; x != 0; x--) + { + DWORD bg; + + bg = bg2rgb[(*spot)&0xff]; + bg = (fg+bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg>>15)]; + spot++; + } + spot += gap; } - spot += gap; } -#endif } //==========================================================================