Antes de poder hablar sobre cosas como los controles de la interfaz de usuario, piense en cómo la computadora puede mostrar algo parecido a los gráficos.
La pantalla es una gran variedad de píxeles (quiero decir ENORME). Una pantalla HD de 1920 x 1080 (no tan grande para una computadora) tiene más de 2 millones de ellos, dispuestos en una cuadrícula rectangular.
Cada píxel corresponde a una dirección en la memoria. Para simplificar, imagine que cada píxel podría mostrar 256 colores diferentes. Eso correspondería a 8 bits de datos que deciden el color de ese píxel. Por ejemplo, el binario 00000000 puede establecer el píxel en negro y 11111111 en blanco. Otros patrones lo configuran en otros colores (explicaré cómo en un momento, pero el blanco y negro serán suficientes por ahora). La dirección más baja podría corresponder al píxel en la parte superior, izquierda y luego la siguiente dirección a la que está al lado (próxima posición horizontal) y así sucesivamente. Una vez que haya avanzado 1.920 direcciones más adelante, estará en el píxel inmediatamente debajo del que está en la parte superior izquierda.
Entonces, si la CPU escribe 00000000 en la dirección de memoria correspondiente a un píxel en particular, el píxel se vuelve negro. Si escribe 11111111 en la misma ubicación, se vuelve blanco. Si configura los más de 2 millones de direcciones de memoria en 11111111, toda la pantalla se vuelve blanca. Entonces, al establecer selectivamente algunas ubicaciones en todos 0, y algunas en todas 1, es capaz de crear una imagen en blanco y negro en la pantalla.
Siguiendo un punto simple (1 píxel), la siguiente cosa gráfica más simple es una línea horizontal o vertical. Esto sería solo un caso de configurar todos los píxeles en una sola fila o columna a negro. Una vez que pueda dibujar líneas, puede dibujar rectángulos.
En este punto, es probable que desee comenzar a organizar su código de tal manera que abstraiga la matriz masiva de píxeles en fragmentos más manejables. Entonces, en lugar de tener que calcular la dirección en la memoria para colocar cada punto, comienza a pensar en términos de coordenadas x, y. Esto se traduce a una dirección en la memoria según sea necesario. Luego, puede definir líneas que comiencen en una posición x, y dada y terminen en otra posición x, y. El algoritmo de dibujo lineal traduce esas coordenadas a direcciones de memoria y pinta los puntos entre esas posiciones. De manera similar, puede definir una estructura de datos de rectángulo como la coordenada x, y de una de sus esquinas (arriba a la izquierda, por ejemplo) más la longitud de cada lado. El código puede usar esa estructura de datos para dibujar líneas a lo largo de los bordes de ese rectángulo, o para completar todos los píxeles dentro de él con un color dado. Todo lo que hace el código es escribir patrones de bits en la memoria.
Las ventanas son solo áreas rectangulares de la pantalla, según lo definido por nuestra estructura de datos de rectángulo. Un montón de código relacionado puede gestionar estas áreas rectangulares y realizar la traducción entre x, y, coordenadas relativas a cada rectángulo y direcciones de memoria de píxeles. Cuando los rectángulos se superponen, puede evitar que se establezcan píxeles cuando están cubiertos por otro rectángulo. Este montón de código es el servidor de ventana. Todo lo que realmente entiende es una lista de rectángulos en un orden de adelante hacia atrás y cómo traducir las coordenadas x, y ‘locales’ de cada ventana a las direcciones de píxeles verdaderas.
La biblioteca de gráficos se utiliza para dibujar el contenido de las ventanas, borrar el fondo, dibujar un marco y una barra de título elegante, etc. Cada ventana tiene un ‘contexto’ asociado, que es solo una estructura de datos que define el sistema de coordenadas y la ruta de recorte dentro de esa ventana (para que el dibujo en la ventana no se extienda a otras partes de la pantalla), así como los parámetros de dibujo como color de línea, grosor, fuente de texto, etc.
Dibujar controles de IU es igual. Las rutinas gráficas que pueden dibujar cosas básicas como líneas, llenar un rectángulo, dibujar una cadena de texto, etc. se utilizan para dibujar el marco del control, el color de fondo, el texto del título, etc. Todo lo que ves en la pantalla está dibujado por el mismo código. En el corazón de cada objeto de la interfaz de usuario hay un código que llama a la biblioteca de gráficos para dibujar su contenido, ya sea una ventana, un control, una cadena de texto, una imagen o cualquier otra cosa. La idea de un ‘control de UI’ está en un nivel muy alto. La apariencia visual se encuentra en un nivel inferior, descrito en términos de líneas, rellenos, cadenas de texto, etc. La biblioteca de gráficos traduce las coordenadas a las direcciones de los píxeles y establece los bits en la memoria de píxeles correspondientes a la línea, el rectángulo, la “primitiva” deseada. círculo, texto, etc. El control de la IU suele ser una clase que tiene propiedades como su rectángulo en relación con la ventana en la que se dibuja, su cadena de título, color de fondo. El objeto mantiene todos estos aspectos relacionados del control asociado, y la biblioteca de gráficos dibuja cada uno cuando el método de dibujo del control lo solicita.
La ‘memoria de píxeles’ es realmente algo llamado RAM de video, o VRAM, y eso es una parte de la RAM principal reservada (por ejemplo, cuando se usa una GPU incorporada), o en una tarjeta gráfica separada. Todo lo que la CPU (o GPU) necesita hacer es establecer los patrones de bits en VRAM correspondientes a un píxel dado, y el hardware entre la VRAM y la pantalla real se encarga de traducir la ‘imagen’ de VRAM a los píxeles brillantes en el monitor .
¿Qué tal los colores? Cada píxel en la pantalla del monitor tiene tres partes: rojo, verde y azul. Estos colores son causados por tintes transparentes que se usan para colorear esa pequeña región en la pantalla, por lo que cuando la luz blanca brilla a través de ellos, muestran solo rojo, verde o azul. Cada subpíxel se puede configurar para que esté completamente encendido, completamente apagado o en algún punto intermedio. De esta manera, un solo píxel completo se puede establecer en cualquier color dentro de la gama de la pantalla. El hardware de la tarjeta gráfica se encarga de tomar los valores de bits en VRAM y calcular exactamente qué cantidad de cada subpíxel rojo, verde y azul se enciende.
Los sistemas gráficos modernos funcionan en color de 32 bits, donde cada parte roja, verde y azul usa 8 bits. También hay un cuarto canal de 8 bits disponible en el modelo gráfico llamado ‘alfa’, que se usa para mezclar colores, pero esto no es parte de VRAM. Por lo tanto, solo se necesitan 24 bits por píxel en VRAM. Eso es 3 bytes, por lo que en realidad el código gráfico establece patrones de bits en los 3 bytes para cada píxel, no un byte como describí anteriormente. Pero el principio es el mismo. (por ejemplo, una pantalla HD de 1920 x 1080 en color de 24 bits realmente usa 6.2 megabytes de VRAM para la imagen de la pantalla, no 2).
Con este sistema, es fácil especificar el color: solo debe establecer la cantidad de rojo, verde y azul para el color con el que desea dibujar, y las rutinas gráficas copian ese valor en VRAM a medida que se dibuja. Así como definimos una estructura de datos para un rectángulo, podemos definir uno para un triplete R, G, B y ese se convierte en uno de los tipos de datos con los que trabajan las rutinas gráficas. En realidad, el color es un tema bastante complicado, porque un monitor a menudo es diferente de otro, y queremos garantizar la consistencia, por lo que el color debe calibrarse, y también hay otros modelos de color además de RGB que podrían ser más convenientes para trabajar. Muy a menudo, el color se describe usando una clase en lugar de un tipo de datos simple, pero al final del proceso, todo son solo valores RGB en VRAM.