Estoy usando el mensaje EM_FORMATRANGE
para procesar el resultado de un control de texto enriquecido en un contexto de dispositivo arbitrario. Sin embargo, al representar en un mapa de bits, los puntos por pulgada del contexto del dispositivo de mapa de bits son los mismos que los DPI del dispositivo de visualización, que son 96 puntos por pulgada. Esto es mucho más bajo de lo que me gustaría renderizar. Prefiero renderizar a un DPI mucho más alto para que el usuario pueda acercar, y quizás imprimir en una impresora de alta resolución más tarde.Errores de redondeo al escalar la salida representada del control de edición enriquecido a través de EM_FORMATRANGE
Sospecho que lo que ocurre es que el control RTF llama a GetDeviceCaps
con LOGPIXELSX
y LOGPIXELSY
para obtener el número de píxeles por pulgada del dispositivo. A continuación, procesa el documento utilizando este valor DPI en un nivel de zoom del 100%. Los dispositivos de visualización de Windows siempre devuelven un valor de 96 ppp, a menos que se utilicen fuentes grandes en el sistema (como se establece en el Panel de control) y la aplicación reconoce los DPI.
Muchos ejemplos en Internet proponen escalar la salida de EM_FORMATRANGE
. Esto es para que se pueda lograr cualquier resolución DPI arbitraria. La mayoría de los ejemplos generalmente implican el uso de SetMapMode
, SetWindowExtEx
y SetViewportExtEx
(por ejemplo, ver http://social.msdn.microsoft.com/Forums/en-us/netfxbcl/thread/37fd1bfb-f07b-421d-9b5e-5f4492ffbbc3). Estas funciones se pueden usar para escalar la salida representada del control de texto enriquecido: por ejemplo, si especifico una escala de 400%, entonces si el control de texto enriquecido representa algo de 5 píxeles de ancho, en realidad tendría 20 píxeles de ancho.
Desafortunadamente, las antiguas funciones de GDI usan enteros en lugar de números de coma flotante. Por ejemplo, suponga que el control RTF decidió que un elemento debería dibujarse en (12.7, 15.3) píxeles. Esto se redondearía a una posición de (13, 15). Estas coordenadas redondeadas se pasan a GDI, que luego amplía la imagen usando la escala especificada por SetMapMode
: para el ejemplo de 400%, sería (13 * 4, 15 * 4) o (52, 60). Pero esto no es exacto: el elemento se hubiera colocado mejor en (12.7 * 4, 15.3 * 4) o (51, 61). La peor parte es que, en algunos casos, el error se vuelve acumulativo.
Creo que esta es la causa subyacente de este error muy notable cuando se escala un poco de texto simple:
El ejemplo anterior es de 8 puntos Segoe UI, reducido a 400% utilizando EM_FORMATRANGE
y SetMapMode
en una Contexto del dispositivo de visualización de 96 DPI. El texto se ha convertido en un tamaño de 32 puntos, pero el espacio entre cada personaje es demasiado alto y no se ve natural.
El ejemplo anterior fue creado en WordPad mediante la introducción de texto en 8 punto Segoe interfaz de usuario y a continuación, utilizando el control de zoom para ajustar a un nivel de zoom 400%. El espacio entre cada personaje parece normal. El mismo resultado exacto se logra con una fuente de 32 puntos y un nivel de zoom del 100%.
Para solucionar este problema, he intentado lo siguiente. Para cada cosa que se intentó, el resultado ha sido idénticamente insatisfactorio cuando se ha escalado al 400%.
- El uso de un conjunto de transformación de escala usando
SetWorldTransform
en lugar de la escala hecho conSetMapMode
ySetWindowExtEx
etc. - Pasando el contexto de dispositivo para un metarchivo a
EM_FORMATRANGE
, y luego escalar el metarchivo más tarde. - Utilizando
SetMapMode
para escalar junto con la representación en un metarchivo, y luego mostrando el metarchivo más tarde sin escalar.
creo que los resultados son siempre satisfactorios debido a que el problema se reduce al hecho de que el control de edición enriquecida es el redondeo al entero más cercano y de la representación de lo que piensa que es un dispositivo de 96 ppp - haciendo caso omiso de las transformaciones en su lugar. Miré en el formato de metarchivo y descubrí que las posiciones de los personajes individuales se almacenan realmente en el metarchivo a resolución de píxel, por eso escalar el metarchivo obviamente no funcionó porque el redondeo ya ha ocurrido en ese punto.
me ocurren dos soluciones reales que trabajarían en torno a este tema:
- Utilice un contexto de dispositivo con unos altos puntos especificados por el usuario por pulgada, de manera que
GetDeviceCaps
devuelve valores diferentes. (Nota: algunos ejemplos proponen usar el dispositivo de la impresora, ya que generalmente tienen una mayor DPI, pero quiero que mi código funcione en sistemas que no tienen una impresora y que pueden renderizar en un búfer fuera de la pantalla). - Una forma de decirle al control rich edit que el contexto del dispositivo tiene un punto diferente por pulgada que el
GetDeviceCaps
.
Cualquier otra cosa parece que todavía estará sujeto a estos errores de redondeo.
¿Alguien (1) tiene una idea de cómo implementar cualquiera de las soluciones que he propuesto, o (2) tiene una idea alternativa de cómo lograr mi objetivo de obtener una salida de alta definición precisa en un búfer?
El escalado de mapa de bits es simplemente intercambiar un mal por otro. La imagen resultante sería borrosa: ¿por qué molestarse en generar una imagen de alto DPI en primer lugar? El método de metarchivo tampoco funcionó para mí, creo que debido a los mismos problemas de error de redondeo, los resultados fueron idénticamente erróneos. –
Solo pensé en dos soluciones posibles: (1) crear un control de edición rico sin ventanas, acercarlo y pedirle que lo renderice en un contexto de dispositivo arbitrario que no sea realmente una ventana. Esta podría ser la mejor manera porque usa una interfaz compatible. (2) enganche la función GetDeviceCaps y mienta acerca de LOGPIXELSX y LOGPIXELSY para su contexto de dispositivo específico, para engañar el rico control de edición en el procesamiento en el DPI deseado. Pero esto sería sin apoyo/indocumentado, obviamente. No he tenido tiempo de investigar ninguna de las soluciones. Si obtienes una solución funcionando, ¡me encantaría saberlo! –
solución (1): según mi experiencia, tratar de dibujar texto gdi (no gdi +) en una superficie que no es una superficie de pantalla le dará malos resultados porque el suavizado de fuentes no funcionará (este problema me ha costado días). solución (2): no tengo ni idea de cómo hacerlo :) ¿tienes xp en ese campo? – Roey