blob: 98d767f56b018d7be6f17532ed9daa9d43cde982 [file] [log] [blame]
/*
* Copyright (c) 2016 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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 <stdio.h>
#include <malloc.h>
#include <math.h>
#include <windows.h>
#include <winuser.h>
#include <Dwrite.h>
#include <jni.h>
#include <jni_util.h>
#include <jlong_md.h>
#include <jdk_util.h>
#include <sizecalc.h>
#include <sun_font_FileFontStrike.h>
#include "fontscalerdefs.h"
extern "C" {
#define FREE \
if (target != NULL) { \
target->Release(); \
}\
if (params != NULL) { \
params->Release(); \
}\
if (defaultParams != NULL) { \
defaultParams->Release(); \
}\
if (face != NULL) { \
face->Release(); \
}\
if (font != NULL) { \
font->Release(); \
}\
if (interop != NULL) { \
interop->Release(); \
}\
if (factory != NULL) { \
factory->Release(); \
}
#define FREE_AND_RETURN \
FREE\
return (jlong)0;
typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, REFIID, IUnknown**);
static BOOL checkedForDirectWriteAvailability = FALSE;
static DWriteCreateFactoryType fDWriteCreateFactory = NULL;
JNIEXPORT jboolean JNICALL
Java_sun_font_FileFontStrike_isDirectWriteAvailable(JNIEnv *env, jclass unused) {
if (!checkedForDirectWriteAvailability) {
checkedForDirectWriteAvailability = TRUE;
HMODULE hDwrite = JDK_LoadSystemLibrary("Dwrite.dll");
if (hDwrite) {
fDWriteCreateFactory = (DWriteCreateFactoryType)GetProcAddress(hDwrite, "DWriteCreateFactory");
}
}
return fDWriteCreateFactory != NULL ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jlong JNICALL
Java_sun_font_FileFontStrike__1getGlyphImageFromWindowsUsingDirectWrite
(JNIEnv *env, jobject unused, jstring fontFamily, jint style, jint size, jint glyphCode,
jint measuringMode, jint renderingMode, jfloat clearTypeLevel, jfloat enhancedContrast, jfloat gamma, jint pixelGeometry) {
// variables cleared by FREE macro
IDWriteFactory* factory = NULL;
IDWriteGdiInterop* interop = NULL;
IDWriteFont* font = NULL;
IDWriteFontFace* face = NULL;
IDWriteRenderingParams* defaultParams = NULL;
IDWriteRenderingParams* params = NULL;
IDWriteBitmapRenderTarget* target = NULL;
LOGFONTW lf;
memset(&lf, 0, sizeof(LOGFONTW));
lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL;
lf.lfItalic = (style & 2) ? TRUE : FALSE;
int nameLen = env->GetStringLength(fontFamily);
if (nameLen >= (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) {
FREE_AND_RETURN
}
env->GetStringRegion(fontFamily, 0, nameLen, lf.lfFaceName);
lf.lfFaceName[nameLen] = '\0';
if (fDWriteCreateFactory == NULL) {
FREE_AND_RETURN
}
HRESULT hr = (*fDWriteCreateFactory)(DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&factory));
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = factory->GetGdiInterop(&interop);
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = interop->CreateFontFromLOGFONT(&lf, &font);
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = font->CreateFontFace(&face);
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = factory->CreateRenderingParams(&defaultParams);
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = factory->CreateCustomRenderingParams(
gamma > 0 && gamma <= 256 ? gamma : defaultParams->GetGamma(),
enhancedContrast >= 0 ? enhancedContrast : defaultParams->GetEnhancedContrast(),
clearTypeLevel >= 0 && clearTypeLevel <= 1 ? clearTypeLevel : defaultParams->GetClearTypeLevel(),
pixelGeometry >= 0 && pixelGeometry <= 2 ? (DWRITE_PIXEL_GEOMETRY)pixelGeometry : defaultParams->GetPixelGeometry(),
renderingMode >= 0 && renderingMode <= 6 ? (DWRITE_RENDERING_MODE)renderingMode : defaultParams->GetRenderingMode(),
&params);
if (FAILED(hr)) {
FREE_AND_RETURN
}
UINT16 indices[] = {(UINT16)glyphCode};
FLOAT advances[] = {0};
DWRITE_GLYPH_OFFSET offsets[] = {{0, 0}};
DWRITE_GLYPH_RUN glyphRun;
glyphRun.fontFace = face;
glyphRun.fontEmSize = (FLOAT)size;
glyphRun.glyphCount = 1;
glyphRun.glyphIndices = indices;
glyphRun.glyphAdvances = advances;
glyphRun.glyphOffsets = offsets;
glyphRun.isSideways = FALSE;
glyphRun.bidiLevel = 0;
DWRITE_FONT_METRICS fontMetrics;
face->GetMetrics(&fontMetrics);
FLOAT pxPerDU = ((FLOAT)size) / fontMetrics.designUnitsPerEm;
DWRITE_GLYPH_METRICS metrics[1];
hr = face->GetDesignGlyphMetrics(indices, 1, metrics, FALSE);
if (FAILED(hr)) {
FREE_AND_RETURN
}
// trying to derive required bitmap size from glyph metrics (adding several spare pixels on each border)
// if that will fail, we'll perform a second attempt based on the output of DrawGlyphRun
int width = (int)((metrics[0].advanceWidth - metrics[0].leftSideBearing - metrics[0].rightSideBearing) * pxPerDU) + 10;
int height = (int)((metrics[0].advanceHeight - metrics[0].topSideBearing - metrics[0].bottomSideBearing) * pxPerDU) + 10;
int x = (int)(-metrics[0].leftSideBearing * pxPerDU) + 5;
int y = (int)((metrics[0].verticalOriginY - metrics[0].topSideBearing) * pxPerDU) + 5;
RECT bbRect;
for (int attempt = 0; attempt < 2 && target == NULL; attempt++) {
hr = interop->CreateBitmapRenderTarget(NULL, width, height, &target);
if (FAILED(hr)) {
FREE_AND_RETURN
}
hr = target->DrawGlyphRun((FLOAT)x,
(FLOAT)y,
measuringMode >= 0 && measuringMode <= 2 ? (DWRITE_MEASURING_MODE)measuringMode : DWRITE_MEASURING_MODE_NATURAL,
&glyphRun,
params,
RGB(255,255,255),
&bbRect);
if (FAILED(hr) || bbRect.left > bbRect.right || bbRect.top > bbRect.bottom
|| attempt > 0 && (bbRect.left < 0 || bbRect.top < 0 || bbRect.right > width || bbRect.bottom > height)) {
FREE_AND_RETURN
}
if (bbRect.left < 0 || bbRect.top < 0 || bbRect.right > width || bbRect.bottom > height) {
target->Release();
target = NULL;
if (bbRect.right > width) width = bbRect.right;
if (bbRect.bottom > height) height = bbRect.bottom;
if (bbRect.left < 0) {
width -= bbRect.left;
x -= bbRect.left;
}
if (bbRect.top < 0) {
height -= bbRect.top;
y -= bbRect.top;
}
}
}
HDC glyphDC = target->GetMemoryDC();
HGDIOBJ glyphBitmap = GetCurrentObject(glyphDC, OBJ_BITMAP);
if (glyphBitmap == NULL) {
FREE_AND_RETURN
}
DIBSECTION dibSection;
if (GetObject(glyphBitmap, sizeof(DIBSECTION), &dibSection) == 0) {
FREE_AND_RETURN
}
int glyphWidth = bbRect.right - bbRect.left;
int glyphHeight = bbRect.bottom - bbRect.top;
int glyphBytesWidth = glyphWidth * 3;
GlyphInfo* glyphInfo = (GlyphInfo*)SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(GlyphInfo), glyphBytesWidth, glyphHeight);
if (glyphInfo == NULL) {
FREE_AND_RETURN
}
glyphInfo->managed = UNMANAGED_GLYPH;
glyphInfo->cellInfo = NULL;
glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo);
glyphInfo->rowBytes = glyphBytesWidth;
glyphInfo->width = glyphWidth;
glyphInfo->height = glyphHeight;
glyphInfo->advanceX = (float)((int)(metrics[0].advanceWidth * pxPerDU + 0.5));
glyphInfo->advanceY = 0;
glyphInfo->topLeftX = (float)(bbRect.left - x);
glyphInfo->topLeftY = (float)(bbRect.top - y);
int srcRowBytes = width * 4;
unsigned char* srcPtr = (unsigned char*) dibSection.dsBm.bmBits + srcRowBytes * bbRect.top;
unsigned char* destPtr = glyphInfo->image;
for (int y = 0; y < glyphHeight; y++) {
srcPtr += bbRect.left * 4;
for (int x = 0; x < glyphWidth; x++) {
// converting from BGRA to RGB
unsigned char b = *srcPtr++;
unsigned char g = *srcPtr++;
unsigned char r = *srcPtr++;
srcPtr++;
*destPtr++ = r;
*destPtr++ = g;
*destPtr++ = b;
}
srcPtr += (width - bbRect.right) * 4;
}
FREE
return ptr_to_jlong(glyphInfo);
}
} // extern "C"