/**************************************************************************** | |

* | |

* ftsdfcommon.c | |

* | |

* Auxiliary data for Signed Distance Field support (body). | |

* | |

* Copyright (C) 2020-2022 by | |

* David Turner, Robert Wilhelm, and Werner Lemberg. | |

* | |

* Written by Anuj Verma. | |

* | |

* This file is part of the FreeType project, and may only be used, | |

* modified, and distributed under the terms of the FreeType project | |

* license, LICENSE.TXT. By continuing to use, modify, or distribute | |

* this file you indicate that you have read the license and | |

* understand and accept it fully. | |

* | |

*/ | |

#include "ftsdf.h" | |

#include "ftsdfcommon.h" | |

/************************************************************************** | |

* | |

* common functions | |

* | |

*/ | |

/* | |

* Original algorithm: | |

* | |

* https://github.com/chmike/fpsqrt | |

* | |

* Use this to compute the square root of a 16.16 fixed point number. | |

*/ | |

FT_LOCAL_DEF( FT_16D16 ) | |

square_root( FT_16D16 val ) | |

{ | |

FT_ULong t, q, b, r; | |

r = (FT_ULong)val; | |

b = 0x40000000L; | |

q = 0; | |

while ( b > 0x40L ) | |

{ | |

t = q + b; | |

if ( r >= t ) | |

{ | |

r -= t; | |

q = t + b; | |

} | |

r <<= 1; | |

b >>= 1; | |

} | |

q >>= 8; | |

return (FT_16D16)q; | |

} | |

/************************************************************************** | |

* | |

* format and sign manipulating functions | |

* | |

*/ | |

/* | |

* Convert 16.16 fixed point values to the desired output format. | |

* In this case we reduce 16.16 fixed point values to normalized | |

* 8-bit values. | |

* | |

* The `max_value` in the parameter is the maximum value in the | |

* distance field map and is equal to the spread. We normalize | |

* the distances using this value instead of computing the maximum | |

* value for the entire bitmap. | |

* | |

* You can use this function to map the 16.16 signed values to any | |

* format required. Do note that the output buffer is 8-bit, so only | |

* use an 8-bit format for `FT_SDFFormat`, or increase the buffer size in | |

* `ftsdfrend.c`. | |

*/ | |

FT_LOCAL_DEF( FT_SDFFormat ) | |

map_fixed_to_sdf( FT_16D16 dist, | |

FT_16D16 max_value ) | |

{ | |

FT_SDFFormat out; | |

FT_16D16 udist; | |

/* normalize the distance values */ | |

dist = FT_DivFix( dist, max_value ); | |

udist = dist < 0 ? -dist : dist; | |

/* Reduce the distance values to 8 bits. */ | |

/* */ | |

/* Since +1/-1 in 16.16 takes the 16th bit, we right-shift */ | |

/* the number by 9 to make it fit into the 7-bit range. */ | |

/* */ | |

/* One bit is reserved for the sign. */ | |

udist >>= 9; | |

/* Since `char` can only store a maximum positive value */ | |

/* of 127 we need to make sure it does not wrap around and */ | |

/* give a negative value. */ | |

if ( dist > 0 && udist > 127 ) | |

udist = 127; | |

if ( dist < 0 && udist > 128 ) | |

udist = 128; | |

/* Output the data; negative values are from [0, 127] and positive */ | |

/* from [128, 255]. One important thing is that negative values */ | |

/* are inverted here, that means [0, 128] maps to [-128, 0] linearly. */ | |

/* More on that in `freetype.h` near the documentation of */ | |

/* `FT_RENDER_MODE_SDF`. */ | |

out = dist < 0 ? 128 - (FT_SDFFormat)udist | |

: (FT_SDFFormat)udist + 128; | |

return out; | |

} | |

/* | |

* Invert the signed distance packed into the corresponding format. | |

* So if the values are negative they will become positive in the | |

* chosen format. | |

* | |

* [Note]: This function should only be used after converting the | |

* 16.16 signed distance values to `FT_SDFFormat`. If that | |

* conversion has not been done, then simply invert the sign | |

* and use the above function to pack the values. | |

*/ | |

FT_LOCAL_DEF( FT_SDFFormat ) | |

invert_sign( FT_SDFFormat dist ) | |

{ | |

return 255 - dist; | |

} | |

/* END */ |