@@ -90,7 +90,7 @@ private static void AddTemperatureToFluidIcon(Fluid fluid) {
9090 fluid . iconSpec =
9191 [
9292 .. fluid . iconSpec ?? [ ] ,
93- .. iconStr . Take ( 4 ) . Select ( ( x , n ) => new FactorioIconPart ( "__.__/" + x ) { y = - 16 , x = ( n * 7 ) - 12 , scale = 0.28f } ) ,
93+ .. iconStr . Take ( 4 ) . Select ( ( x , n ) => new FactorioIconPart ( "__.__/" + x ) { size = 64 , y = - 32 , x = ( n * 14 ) - 24 , scale = 0.28f } ) ,
9494 ] ;
9595 }
9696
@@ -244,8 +244,9 @@ private void RenderIcons() {
244244 }
245245
246246 private unsafe Icon CreateIconFromSpec ( Dictionary < ( string mod , string path ) , IntPtr > cache , params FactorioIconPart [ ] spec ) {
247- const int iconSize = IconCollection . IconSize ;
248- nint targetSurface = SDL . SDL_CreateRGBSurfaceWithFormat ( 0 , iconSize , iconSize , 0 , SDL . SDL_PIXELFORMAT_RGBA8888 ) ;
247+ const int cachedIconSize = IconCollection . IconSize ;
248+ int renderSize = MathUtils . Round ( spec [ 0 ] . size * spec [ 0 ] . scale ) ;
249+ nint targetSurface = SDL . SDL_CreateRGBSurfaceWithFormat ( 0 , renderSize , renderSize , 0 , SDL . SDL_PIXELFORMAT_RGBA8888 ) ;
249250 _ = SDL . SDL_SetSurfaceBlendMode ( targetSurface , SDL . SDL_BlendMode . SDL_BLENDMODE_BLEND ) ;
250251
251252 foreach ( var icon in spec ) {
@@ -272,10 +273,6 @@ private unsafe Icon CreateIconFromSpec(Dictionary<(string mod, string path), Int
272273 image = SDL . SDL_ConvertSurfaceFormat ( old , SDL . SDL_PIXELFORMAT_RGBA8888 , 0 ) ;
273274 SDL . SDL_FreeSurface ( old ) ;
274275 }
275-
276- if ( surface . h > iconSize * 2 ) {
277- image = SoftwareScaler . DownscaleIcon ( image , iconSize ) ;
278- }
279276 }
280277 cache [ modPath ] = image ;
281278 }
@@ -287,10 +284,10 @@ private unsafe Icon CreateIconFromSpec(Dictionary<(string mod, string path), Int
287284 }
288285
289286 ref var sdlSurface = ref RenderingUtils . AsSdlSurface ( image ) ;
290- int targetSize = icon . scale == 1f ? iconSize : MathUtils . Ceil ( icon . size * icon . scale ) * ( iconSize / 32 ) ; // TODO research formula
287+ int targetSize = MathUtils . Round ( icon . size * icon . scale ) ;
291288 _ = SDL . SDL_SetSurfaceColorMod ( image , MathUtils . FloatToByte ( icon . r ) , MathUtils . FloatToByte ( icon . g ) , MathUtils . FloatToByte ( icon . b ) ) ;
292289 //SDL.SDL_SetSurfaceAlphaMod(image, MathUtils.FloatToByte(icon.a));
293- int basePosition = ( iconSize - targetSize ) / 2 ;
290+ int basePosition = ( renderSize - targetSize ) / 2 ;
294291 SDL . SDL_Rect targetRect = new SDL . SDL_Rect {
295292 x = basePosition ,
296293 y = basePosition ,
@@ -300,14 +297,14 @@ private unsafe Icon CreateIconFromSpec(Dictionary<(string mod, string path), Int
300297
301298 if ( icon . x != 0 ) {
302299 // These two formulas have variously multiplied icon.x (or icon.y) by a scaling factor of iconSize / icon.size,
303- // iconSize * icon.scale, or iconSize. ( const int iconSize = 32)
300+ // iconSize * icon.scale, or iconSize (iconSize is now const int cachedIconSize = 32).
304301 // Presumably the scaling factor had a purpose, but I can't find it. Py and Vanilla objects (e.g. Recipe.Moss-1 and
305302 // Entity.lane-splitter) draw correctly after removing the scaling factor.
306- targetRect . x = MathUtils . Clamp ( targetRect . x + MathUtils . Round ( icon . x ) , 0 , iconSize - targetRect . w ) ;
303+ targetRect . x = MathUtils . Clamp ( targetRect . x + MathUtils . Round ( icon . x ) , 0 , renderSize - targetRect . w ) ;
307304 }
308305
309306 if ( icon . y != 0 ) {
310- targetRect . y = MathUtils . Clamp ( targetRect . y + MathUtils . Round ( icon . y ) , 0 , iconSize - targetRect . h ) ;
307+ targetRect . y = MathUtils . Clamp ( targetRect . y + MathUtils . Round ( icon . y ) , 0 , renderSize - targetRect . h ) ;
311308 }
312309
313310 SDL . SDL_Rect srcRect = new SDL . SDL_Rect {
@@ -317,6 +314,9 @@ private unsafe Icon CreateIconFromSpec(Dictionary<(string mod, string path), Int
317314 _ = SDL . SDL_BlitScaled ( image , ref srcRect , targetSurface , ref targetRect ) ;
318315 }
319316
317+ if ( RenderingUtils . AsSdlSurface ( targetSurface ) . h > cachedIconSize * 2 ) {
318+ targetSurface = SoftwareScaler . DownscaleIcon ( targetSurface , cachedIconSize ) ;
319+ }
320320 return IconCollection . AddIcon ( targetSurface ) ;
321321 }
322322
@@ -742,19 +742,25 @@ private void DeserializeLocation(LuaTable table, ErrorCollector collector) {
742742 }
743743
744744 if ( table . Get ( "icon" , out string ? s ) ) {
745- target . iconSpec = [ new FactorioIconPart ( s ) { size = table . Get ( "icon_size" , 64f ) } ] ;
745+ target . iconSpec = [ new FactorioIconPart ( s ) { size = table . Get ( "icon_size" , 64 ) } ] ;
746746 }
747747 else if ( table . Get ( "icons" , out LuaTable ? iconList ) ) {
748748 target . iconSpec = [ .. iconList . ArrayElements < LuaTable > ( ) . Select ( x => {
749749 if ( ! x . Get ( "icon" , out string ? path ) ) {
750750 throw new NotSupportedException ( $ "One of the icon layers for { name } does not have a path.") ;
751751 }
752752
753- FactorioIconPart part = new ( path ) {
754- size = x . Get ( "icon_size" , 64f ) ,
755- scale = x . Get ( "scale" , 1f )
753+ int expectedSize = target switch {
754+ // These are the only expected sizes for icons we render.
755+ // New classes of icons (e.g. achievements) will need new cases to make IconParts with default scale render correctly.
756+ // https://lua-api.factorio.com/latest/types/IconData.html
757+ Technology => 256 ,
758+ _ => 64
756759 } ;
757760
761+ FactorioIconPart part = new ( path ) { size = x . Get ( "icon_size" , 64 ) } ;
762+ part . scale = x . Get ( "scale" , expectedSize / 2f / part . size ) ;
763+
758764 if ( x . Get ( "shift" , out LuaTable ? shift ) ) {
759765 part . x = shift . Get < float > ( 1 ) ;
760766 part . y = shift . Get < float > ( 2 ) ;
0 commit comments