/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include "sunfontids.h"
#include "sun_font_FreetypeFontScaler.h"
#include<stdlib.h>
#include <math.h>
#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_BBOX_H
#include FT_SIZES_H
#include FT_OUTLINE_H
#include FT_SYNTHESIS_H
#include "fontscaler.h"
typedef struct {
/* Important note:
JNI forbids sharing same env between different threads.
We are safe, because pointer is overwritten every time we get into
JNI call (see setupFTContext).
Pointer is used by font data reading callbacks
such as ReadTTFontFileFunc.
NB: We may consider switching to JNI_GetEnv. */
unsigned char* fontData;
unsigned fontDataOffset;
unsigned fontDataLength;
unsigned fileSize;
} FTScalerInfo;
typedef struct FTScalerContext {
int pathType;
#ifdef DEBUG
/* These are referenced in the freetype sources if DEBUG macro is defined.
To simplify work with debuging version of freetype we define
them here. */
int z_verbose;
void z_error(char *s) {}
#endif
/**************** Error handling utilities *****************/
}
void *stream;
if (scalerInfo == NULL)
return;
//apparently Done_Face will only close the stream
// but will not relase the memory of stream structure.
// We need to free it explicitly to avoid leak.
//Direct access to the stream field might be not ideal solution as
// it is considred to be "private".
//Alternatively we could have stored pointer to the structure
// in the scalerInfo but this will increase size of the structure
// for no good reason
}
}
}
}
/* invalidates state of java scaler object */
}
/******************* I/O handlers ***************************/
/* NB: is it ever called? */
}
unsigned long offset,
unsigned char* destBuffer,
unsigned long numBytes)
{
int bread = 0;
if (numBytes == 0) return 0;
/* Large reads will bypass the cache and data copying */
if (numBytes > FILEDATACACHESIZE) {
/* Loop until the read succeeds (or EOF).
* This should improve robustness in the event of a problem in
* the I/O system. If we find that we ever end up spinning here
* we are going to have to do some serious work to recover.
* Just returning without reading the data will cause a crash.
*/
while (bread == 0) {
}
return bread;
} else {
/* We probably hit bug bug 4845371. For reasons that
* are currently unclear, the call stacks after the initial
* createScaler call that read large amounts of data seem to
* be OK and can create the byte buffer above, but this code
* is here just in case.
* 4845371 is fixed now so I don't expect this code path to
* ever get called but its harmless to leave it here on the
* small chance its needed.
*/
return numBytes;
}
} /* Do we have a cache hit? */
{
return numBytes;
} else {
/* Must fill the cache */
/* Loop until all the read succeeds (or EOF).
* This should improve robustness in the event of a problem in
* the I/O system. If we find that we ever end up spinning here
* we are going to have to do some serious work to recover.
* Just returning without reading the data will cause a crash.
*/
while (bread == 0) {
}
return numBytes;
}
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: initNativeScaler
*/
int error;
if (scalerInfo == NULL)
return 0;
scalerInfo->fontDataOffset = 0;
scalerInfo->fontDataLength = 0;
/*
We can consider sharing freetype library between different
scalers. However, Freetype docs suggest to use different libraries
for different threads. Also, our architecture implies that single
FontScaler object is shared for for different sizes/transforms/styles
of the same font.
On other hand these methods can not be concurrently executed
becaused they are "synchronized" in java.
*/
if (error) {
return 0;
}
&scalerInfo->face);
}
}
} else { /* Truetype */
&scalerInfo->face);
}
}
}
}
if (error) {
}
return 0;
}
return ptr_to_jlong(scalerInfo);
}
static double euclidianDistance(double a, double b) {
if (a < 0) a=-a;
if (b < 0) b=-b;
if (a == 0) return b;
if (b == 0) return a;
return sqrt(a*a+b*b);
}
return (jlong) 0;
}
if (ptsz < 1.0) {
//text can not be smaller than 1 point
ptsz = 1.0;
}
/* If using algorithmic styling, the base values are
* boldness = 1.0, italic = 0.0.
*/
return ptr_to_jlong(context);
}
int errCode = 0;
if (errCode == 0) {
}
}
return errCode;
}
/* ftsynth.c uses (0x10000, 0x06000, 0x0, 0x10000) matrix to get oblique
outline. Therefore x coordinate will change by 0x06000*y.
Note that y coordinate does not change. */
/*
* Class: sun_font_FreetypeFontScaler
* Method: getFontMetricsNative
*/
int errCode;
}
if (errCode) {
return metrics;
}
/* This is ugly and has to be reworked.
Freetype provide means to add style to glyph but
it seems there is no way to adjust metrics accordingly.
So, we have to do adust them explicitly and stay consistent with what
freetype does to outlines. */
/* For bolding glyphs are not just widened. Height is also changed
(see ftsynth.c).
TODO: In vertical direction we could do better job and adjust metrics
proportionally to glyoh shape. */
}
/**** Note: only some metrics are affected by styling ***/
/* ascent */
ax = 0;
/* descent */
dx = 0;
/* baseline */
/* leading */
lx = 0;
/* max advance */
2*bmodifier +
my = 0;
return metrics;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphAdvanceNative
*/
/* This method is rarely used because requests for metrics are usually
coupled with request for bitmap and to large extend work can be reused
(to find out metrics we need to hint glyph).
So, we typically go through getGlyphImage code path.
For initial freetype implementation we delegate
all work to getGlyphImage but drop result image.
This is waste of work related to scan conversion and conversion from
freetype format to our format but for now this seems to be ok.
NB: investigate performance benefits of refactoring code
to avoid unnecesary work with bitmaps. */
return advance;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphMetricsNative
*/
/* As initial implementation we delegate all work to getGlyphImage
but drop result image. This is clearly waste of resorces.
TODO: investigate performance benefits of refactoring code
by avoiding bitmap generation and conversion from FT
bitmap format. */
}
return glyphInfo;
}
void* dstImage, int dstRowBytes,
int i, j;
while (height--) {
unsigned srcValue;
srcRow += srcRowBytes;
dstRow += dstRowBytes;
for (i = 0; i < wholeByteCount; i++) {
for (j = 0; j < 8; j++) {
srcValue <<= 1;
}
}
if (remainingBitsCount) {
for (j = 0; j < remainingBitsCount; j++) {
srcValue <<= 1;
}
}
}
}
int i;
while (height--) {
unsigned srcValue;
srcRow += srcRowBytes;
dstRow += dstRowBytes;
for (i = 0; i < width; i++) {
}
}
}
/* We need it because FT rows are often padded to 4 byte boundaries
and our internal format is not padded */
void* dstImage, int dstRowBytes,
while (height--) {
srcRow += srcRowBytes;
dstRow += dstRowBytes;
}
}
/* We need it because FT rows are often padded to 4 byte boundaries
and our internal format is not padded */
void* dstImage, int dstRowBytes,
int i;
while (height > 0) {
for (i = 0; i < width; i++) {
srcByte++;
}
dstRow += dstRowBytes;
height -= 3;
}
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphImageNative
*/
int glyph_index;
return ptr_to_jlong(getNullGlyphImage());
}
if (error) {
return ptr_to_jlong(getNullGlyphImage());
}
/* if algorithmic styling is required then we do not request bitmap */
}
/* NB: in case of non identity transform
we might also prefer to disable transform before hinting,
and apply it explicitly after hinting is performed.
Or we can disable hinting. */
/* select appropriate hinting mode */
} else {
}
renderFlags |= target;
if (error) {
//do not destroy scaler yet.
//this can be problem of particular context (e.g. with bad transform)
return ptr_to_jlong(getNullGlyphImage());
}
/* apply styles */
}
}
/* generate bitmap if it is not done yet
e.g. if algorithmic styling is performed and style was added to outline */
}
return ptr_to_jlong(glyphInfo);
}
}
} else {
} else {
}
}
if (imageSize == 0) {
} else {
//convert result to output format
//output format is either 3 bytes per pixel (for subpixel modes)
// or 1 byte per pixel for AA and B&W
/* convert from 8 pixels per byte to 1 byte per pixel */
height);
/* byte per pixel to byte per pixel => just copy */
/* 4 bits per pixel to byte per pixel */
height);
/* 3 bytes per pixel to 3 bytes per pixel */
height);
/* 3 bytes per pixel to 3 bytes per pixel */
width*3,
height);
} else {
}
}
return ptr_to_jlong(glyphInfo);
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getLayoutTableCacheNative
* Signature: (J)J
*/
if (scalerInfo == NULL) {
return 0L;
}
// init layout table cache in font
// we're assuming the font is a file font and moreover it is Truetype font
// otherwise we shouldn't be able to get here...
}
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: disposeNativeScaler
* Signature: (J)V
*/
/* Freetype functions *may* cause callback to java
that can use cached values. Make sure our cache is up to date.
NB: scaler context is not important at this point, can use NULL. */
if (errCode) {
return;
}
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getNumGlyphsNative
* Signature: ()I
*/
/* null scaler can render 1 glyph - "missing glyph" with code 0
(all glyph codes requested by user are mapped to code 0 at
validation step) */
return (jint) 1;
}
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getMissingGlyphCodeNative
* Signature: ()I
*/
/* Is it always 0 for freetype? */
return 0;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphCodeNative
* Signature: (C)I
*/
int errCode;
return 0;
}
/* Freetype functions *may* cause callback to java
that can use cached values. Make sure our cache is up to date.
Scaler context is not important here, can use NULL. */
if (errCode) {
return 0;
}
}
int renderFlags;
int glyph_index;
if (glyphCode >= INVISIBLE_GLYPHS ||
return NULL;
}
if (error) {
return NULL;
}
if (error) {
return NULL;
}
/* apply styles */
}
}
-FloatToF26Dot6(ypos));
}
/* Types of GeneralPath segments.
TODO: pull constants from other place? */
#define SEG_MOVETO 0
#define WIND_NON_ZERO 0
/* Placeholder to accumulate GeneralPath data */
typedef struct {
} GPData;
/* returns 0 on failure */
/* we may have up to N intermediate points per contour
(and for each point can actually cause new curve to be generated)
In addition we can also have 2 extra point per outline.
*/
//up to n-1 intermediate points
/* first usage - allocate space and intialize all fields */
using the non-zero winding rule. */
} else {
/* do we have enough space? */
}
}
}
/* failure if any of mallocs failed */
return 0;
else
return 1;
}
int i, j;
jfloat x, y;
j = 0;
/* If bit 0 is unset, the point is "off" the curve,
i.e., a Bezier control point, while it is "on" when set. */
very first point */
/* add segment */
} else {
}
} else {
very first point */
/* just skip first point. Adhoc heuristic? */
continue;
} else {
}
/* Bit 1 is meaningful for 'off' points only.
If set, it indicates a third-order Bezier arc control
point; and a second-order control point if unset. */
} else {
/* two successive conic "off" points forces the rasterizer
to create (during the scan-line conversion process
exclusively) a virtual "on" point amidst them, at their
exact middle. This greatly facilitates the definition of
successive conic Bezier arcs. Moreover, it is the way
outlines are described in the TrueType specification. */
if (current_type == SEG_QUADTO) {
}
}
}
if (current_type == SEG_QUADTO &&
} else {
}
j++;
}
}
/* If set to 1, the outline will be filled using the even-odd fill rule */
}
}
}
}
}
return gp;
}
return gp;
}
}
return gp;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphOutlineNative
*/
xpos,
ypos);
}
return gp;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphOutlineBoundsNative
*/
int error;
/* it is legal case, e.g. invisible glyph */
return bounds;
}
//convert bbox
} else {
}
return bounds;
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getGlyphVectorOutlineNative
*/
int i;
}
// We reach here if:
// 1. numGlyphs <= 0,
// 2. overflow check failed, or
// 3. malloc failed.
return gp;
}
for (i=0; i<numGlyphs;i++) {
if (glyphs[i] >= INVISIBLE_GLYPHS) {
continue;
}
glyphs[i],
continue;
}
outline->n_contours)) {
break;
}
}
return gp;
}
}
}
/* Freetype doc says:
The number of font units per EM square for this face.
This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
Only relevant for scalable formats.
However, layout engine might be not tested with anything but 2048.
NB: test it! */
if (s != NULL) {
return s->face->units_per_EM;
}
return 2048;
}
/* This native method is called by the OpenType layout engine. */
jfloat x=0, y=0;
}
sunFontIDs.pt2DFloatCtr, x, y);
}