diff --git a/graphics_hd/logo.png b/graphics_hd/logo.png new file mode 100644 index 000000000..f321e98e5 Binary files /dev/null and b/graphics_hd/logo.png differ diff --git a/src/cdogs/font.c b/src/cdogs/font.c index ecea1b157..57f031bf9 100644 --- a/src/cdogs/font.c +++ b/src/cdogs/font.c @@ -108,10 +108,11 @@ void FontLoad( pos.x += step.x, x++, chars++) { Pic p; + // TODO: HD fonts PicLoad( &p, f->Size, svec2i_add(pos, svec2i(f->Padding.Left, f->Padding.Top)), - image); + image, false); if (chars == ' ') { PicShrink(&p, spaceSize, svec2i_zero()); diff --git a/src/cdogs/map_archive.c b/src/cdogs/map_archive.c index 3a558abae..b2fa5dc0e 100644 --- a/src/cdogs/map_archive.c +++ b/src/cdogs/map_archive.c @@ -227,7 +227,9 @@ static void LoadArchivePics(PicManager *pm, map_t cc, const char *archive) { char path[CDOGS_PATH_MAX]; sprintf(path, "%s/graphics", archive); - PicManagerLoadDir(pm, path, NULL, pm->customPics, pm->customSprites); + PicManagerLoadDir(pm, path, NULL, pm->customPics, pm->customSprites, false); + sprintf(path, "%s/graphics_hd", archive); + PicManagerLoadDir(pm, path, NULL, pm->customPics, pm->customSprites, true); CharSpriteClassesLoadDir(cc, archive); } diff --git a/src/cdogs/pic.c b/src/cdogs/pic.c index 11daa1f51..4011b291b 100644 --- a/src/cdogs/pic.c +++ b/src/cdogs/pic.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2013-2016, 2018-2019 Cong Xu + Copyright (c) 2013-2016, 2018-2019, 2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -56,12 +56,27 @@ Uint32 ColorToPixel( ((Uint32)color.a << aShift); } +// Get the true pixel size of the pic +static struct vec2i PicPixelSize(const Pic *p) +{ + if (p->isHD) + { + return svec2i_scale(p->size, 2); + } + return p->size; +} void PicLoad( - Pic *p, const struct vec2i size, const struct vec2i offset, const SDL_Surface *image) + Pic *p, const struct vec2i size, const struct vec2i offset, const SDL_Surface *image, const bool isHD) { memset(p, 0, sizeof *p); p->size = size; + // Pretend to be half the size for HD pics + p->isHD = isHD; + if (isHD) + { + p->size = svec2i_scale_divide(p->size, 2); + } p->offset = svec2i_zero(); CMALLOC(p->Data, size.x * size.y * sizeof *((Pic *)0)->Data); if (p->Data == NULL) @@ -134,16 +149,17 @@ bool PicTryMakeTex(Pic *p) } } } + const struct vec2i size = PicPixelSize(p); p->Tex = TextureCreate( gGraphicsDevice.gameWindow.renderer, SDL_TEXTUREACCESS_STATIC, - p->size, SDL_BLENDMODE_NONE, 255); + size, SDL_BLENDMODE_NONE, 255); if (p->Tex == NULL) { LOG(LM_GFX, LL_ERROR, "cannot create texture: %s", SDL_GetError()); return false; } if (SDL_UpdateTexture( - p->Tex, NULL, p->Data, p->size.x * sizeof(Uint32)) != 0) + p->Tex, NULL, p->Data, size.x * sizeof(Uint32)) != 0) { LOG(LM_GFX, LL_ERROR, "cannot update texture: %s", SDL_GetError()); return false; @@ -176,10 +192,12 @@ bool PicTryMakeTex(Pic *p) Pic PicCopy(const Pic *src) { Pic p = *src; - const size_t size = p.size.x * p.size.y * sizeof *p.Data; + const struct vec2i psize = PicPixelSize(src); + const size_t size = psize.x * psize.y * sizeof *p.Data; CMALLOC(p.Data, size); memcpy(p.Data, src->Data, size); p.Tex = NULL; + p.isHD = src->isHD; return p; } @@ -224,13 +242,14 @@ bool PicIsNone(const Pic *pic) void PicTrim(Pic *pic, const bool xTrim, const bool yTrim) { // Scan all pixels looking for the min/max of x and y - struct vec2i min = pic->size; + const struct vec2i size = PicPixelSize(pic); + struct vec2i min = size; struct vec2i max = svec2i_zero(); - for (struct vec2i pos = svec2i_zero(); pos.y < pic->size.y; pos.y++) + for (struct vec2i pos = svec2i_zero(); pos.y < size.y; pos.y++) { - for (pos.x = 0; pos.x < pic->size.x; pos.x++) + for (pos.x = 0; pos.x < size.x; pos.x++) { - const Uint32 pixel = *(pic->Data + pos.x + pos.y * pic->size.x); + const Uint32 pixel = *(pic->Data + pos.x + pos.y * size.x); if (pixel > 0) { min.x = MIN(min.x, pos.x); @@ -241,7 +260,7 @@ void PicTrim(Pic *pic, const bool xTrim, const bool yTrim) } } // If no opaque pixels found, don't trim - struct vec2i newSize = pic->size; + struct vec2i newSize = size; struct vec2i offset = svec2i_zero(); if (min.x < max.x && min.y < max.y) { @@ -281,41 +300,21 @@ void PicShrink(Pic *pic, const struct vec2i size, const struct vec2i offset) CFREE(pic->Data); pic->Data = newData; pic->size = size; + if (pic->isHD) + { + pic->size = svec2i_scale_divide(pic->size, 2); + } pic->offset = svec2i_zero(); PicTryMakeTex(pic); } -bool PicPxIsEdge(const Pic *pic, const struct vec2i pos, const bool isPixel) -{ - const bool isTopOrBottomEdge = pos.y == -1 || pos.y == pic->size.y; - const bool isLeftOrRightEdge = pos.x == -1 || pos.x == pic->size.x; - const bool isLeft = - pos.x > 0 && !isTopOrBottomEdge && - PIXEL2COLOR(*(pic->Data + pos.x - 1 + pos.y * pic->size.x)).a; - const bool isRight = - pos.x < pic->size.x - 1 && !isTopOrBottomEdge && - PIXEL2COLOR(*(pic->Data + pos.x + 1 + pos.y * pic->size.x)).a; - const bool isAbove = - pos.y > 0 && !isLeftOrRightEdge && - PIXEL2COLOR(*(pic->Data + pos.x + (pos.y - 1) * pic->size.x)).a; - const bool isBelow = - pos.y < pic->size.y - 1 && !isLeftOrRightEdge && - PIXEL2COLOR(*(pic->Data + pos.x + (pos.y + 1) * pic->size.x)).a; - if (isPixel) - { - return !(isLeft && isRight && isAbove && isBelow); - } - else - { - return isLeft || isRight || isAbove || isBelow; - } -} color_t PicGetRandomColor(const Pic *p) { // Get a random non-transparent pixel from the pic + const struct vec2i size = PicPixelSize(p); for (;;) { - const uint32_t px = p->Data[rand() % (p->size.x * p->size.y)]; + const uint32_t px = p->Data[rand() % (size.x * size.y)]; const color_t c = PIXEL2COLOR(px); if (c.a > 0) { @@ -332,16 +331,24 @@ void PicRender( Rect2i src = Rect2iNew( svec2i_max(srcRect.Pos, svec2i_zero()), svec2i_zero() ); - src.Size = svec2i_is_zero(srcRect.Size) ? p->size : - svec2i_min(svec2i_subtract(srcRect.Size, src.Pos), p->size); + const struct vec2i srcSize = PicPixelSize(p); + src.Size = svec2i_is_zero(srcRect.Size) ? srcSize : + svec2i_min(svec2i_subtract(srcRect.Size, src.Pos), srcSize); Rect2i dest = Rect2iNew(pos, src.Size); // Apply scale to render dest - if (!svec2_is_equal(scale, svec2_one())) + const bool unscaled = svec2_is_equal(scale, svec2_one()); + const struct vec2 destScale = svec2_scale(scale, p->isHD ? 0.5f : 1); + if (!unscaled) + { + dest.Pos.x -= (mint_t)MROUND((destScale.x - 1) * src.Size.x / 2); + dest.Pos.y -= (mint_t)MROUND((destScale.y - 1) * src.Size.y / 2); + dest.Size.x = (mint_t)MROUND(src.Size.x * destScale.x); + dest.Size.y = (mint_t)MROUND(src.Size.y * destScale.y); + } + else if (p->isHD) { - dest.Pos.x -= (mint_t)MROUND((scale.x - 1) * src.Size.x / 2); - dest.Pos.y -= (mint_t)MROUND((scale.y - 1) * src.Size.y / 2); - dest.Size.x = (mint_t)MROUND(src.Size.x * scale.x); - dest.Size.y = (mint_t)MROUND(src.Size.y * scale.y); + dest.Size.x = (mint_t)MROUND(src.Size.x * destScale.x); + dest.Size.y = (mint_t)MROUND(src.Size.y * destScale.y); } const double angle = ToDegrees(radians); TextureRender(p->Tex, r, src, dest, mask, angle, flip); diff --git a/src/cdogs/pic.h b/src/cdogs/pic.h index e7dd67232..9582608b4 100644 --- a/src/cdogs/pic.h +++ b/src/cdogs/pic.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2013-2016, 2018-2019 Cong Xu + Copyright (c) 2013-2016, 2018-2019, 2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ typedef struct { struct vec2i size; struct vec2i offset; + bool isHD; Uint32 *Data; SDL_Texture *Tex; } Pic; @@ -48,7 +49,7 @@ Uint32 ColorToPixel( void PicLoad( Pic *p, const struct vec2i size, const struct vec2i offset, - const SDL_Surface *image); + const SDL_Surface *image, const bool isHD); bool PicTryMakeTex(Pic *p); Pic PicCopy(const Pic *src); void PicFree(Pic *pic); @@ -58,7 +59,6 @@ bool PicIsNone(const Pic *pic); void PicTrim(Pic *pic, const bool xTrim, const bool yTrim); void PicShrink(Pic *pic, const struct vec2i size, const struct vec2i offset); -bool PicPxIsEdge(const Pic *pic, const struct vec2i pos, const bool isPixel); color_t PicGetRandomColor(const Pic *p); void PicRender( diff --git a/src/cdogs/pic_manager.c b/src/cdogs/pic_manager.c index fe3d6f659..ae8f2ffc8 100644 --- a/src/cdogs/pic_manager.c +++ b/src/cdogs/pic_manager.c @@ -1,7 +1,7 @@ /* C-Dogs SDL A port of the legendary (and fun) action/arcade cdogs. - Copyright (c) 2013-2020 Cong Xu + Copyright (c) 2013-2020, 2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -39,6 +39,7 @@ #include "log.h" #define GRAPHICS_DIR "graphics" +#define GRAPHICS_HD_DIR "graphics_hd" PicManager gPicManager; @@ -69,7 +70,7 @@ static NamedPic *AddNamedPic(map_t pics, const char *name, const Pic *p); static NamedSprites *AddNamedSprites(map_t sprites, const char *name); static void AfterAdd(PicManager *pm); static void PicManagerAdd( - map_t pics, map_t sprites, const char *name, SDL_Surface *imageIn) + map_t pics, map_t sprites, const char *name, SDL_Surface *imageIn, const bool isHD) { char buf[CDOGS_FILENAME_MAX]; const char *dot = strrchr(name, '.'); @@ -134,7 +135,7 @@ static void PicManagerAdd( { pic = &np->pic; } - PicLoad(pic, size, offset, image); + PicLoad(pic, size, offset, image, isHD); if (strncmp("chars/", buf, strlen("chars/")) == 0) { @@ -187,7 +188,7 @@ static void PicManagerAdd( void PicManagerLoadDir( PicManager *pm, const char *path, const char *prefix, map_t pics, - map_t sprites) + map_t sprites, const bool isHD) { tinydir_dir dir; if (tinydir_open(&dir, path) == -1) @@ -234,7 +235,7 @@ void PicManagerLoadDir( { PathGetBasenameWithoutExtension(buf, file.name); } - PicManagerAdd(pics, sprites, buf, data); + PicManagerAdd(pics, sprites, buf, data, isHD); } } rwops->close(rwops); @@ -245,11 +246,11 @@ void PicManagerLoadDir( { char buf[CDOGS_PATH_MAX]; sprintf(buf, "%s/%s", prefix, file.name); - PicManagerLoadDir(pm, file.path, buf, pics, sprites); + PicManagerLoadDir(pm, file.path, buf, pics, sprites, isHD); } else { - PicManagerLoadDir(pm, file.path, file.name, pics, sprites); + PicManagerLoadDir(pm, file.path, file.name, pics, sprites, isHD); } } } @@ -261,7 +262,9 @@ void PicManagerLoad(PicManager *pm) { char buf[CDOGS_PATH_MAX]; GetDataFilePath(buf, GRAPHICS_DIR); - PicManagerLoadDir(pm, buf, NULL, pm->pics, pm->sprites); + PicManagerLoadDir(pm, buf, NULL, pm->pics, pm->sprites, false); + GetDataFilePath(buf, GRAPHICS_HD_DIR); + PicManagerLoadDir(pm, buf, NULL, pm->pics, pm->sprites, true); } static void FindStylePics( diff --git a/src/cdogs/pic_manager.h b/src/cdogs/pic_manager.h index eaee7d1e8..2a7f07721 100644 --- a/src/cdogs/pic_manager.h +++ b/src/cdogs/pic_manager.h @@ -1,7 +1,7 @@ /* C-Dogs SDL A port of the legendary (and fun) action/arcade cdogs. - Copyright (c) 2013-2016, 2018-2019, 2023 Cong Xu + Copyright (c) 2013-2016, 2018-2019, 2023-2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -52,7 +52,7 @@ void PicManagerInit(PicManager *pm); void PicManagerLoad(PicManager *pm); void PicManagerLoadDir( PicManager *pm, const char *path, const char *prefix, - map_t pics, map_t sprites); + map_t pics, map_t sprites, const bool isHD); void PicManagerClearCustom(PicManager *pm); void PicManagerTerminate(PicManager *pm); void PicManagerReloadTextures(PicManager *pm); diff --git a/src/cdogsed/editor_brush.c b/src/cdogsed/editor_brush.c index 46e8410a6..83aa16f17 100644 --- a/src/cdogsed/editor_brush.c +++ b/src/cdogsed/editor_brush.c @@ -668,7 +668,7 @@ bool EditorBrushTryLoadGuideImage(EditorBrush *b, const char *filename) return false; SDL_Surface *sc = SDL_ConvertSurface(s, gGraphicsDevice.Format, 0); SDL_FreeSurface(s); - PicLoad(&b->GuideImagePic, svec2i(sc->w, sc->h), svec2i_zero(), sc); + PicLoad(&b->GuideImagePic, svec2i(sc->w, sc->h), svec2i_zero(), sc, false); SDL_FreeSurface(sc); return true; } diff --git a/src/tests/pic_test.c b/src/tests/pic_test.c index c8228b740..fc35c051e 100644 --- a/src/tests/pic_test.c +++ b/src/tests/pic_test.c @@ -31,7 +31,7 @@ FEATURE(PicLoad, "Pic load") Pic p; p.size = svec2i(1, 1); p.offset = svec2i_zero(); - PicLoad(&p, p.size, svec2i_zero(), image); + PicLoad(&p, p.size, svec2i_zero(), image, false); THEN("the loaded pic should have values that match"); const color_t c = PIXEL2COLOR(p.Data[0]);