mirror of https://gitlab.com/nakst/essence
652 lines
24 KiB
C++
652 lines
24 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
#ifndef IMPLEMENTATION
|
|
|
|
#define CURSOR_SHADOW_OFFSET_X (0)
|
|
#define CURSOR_SHADOW_OFFSET_Y (1)
|
|
#define CURSOR_PADDING_T (2)
|
|
#define CURSOR_PADDING_B (4)
|
|
#define CURSOR_PADDING_L (3)
|
|
#define CURSOR_PADDING_R (3)
|
|
|
|
struct Surface : EsPaintTarget {
|
|
bool Resize(size_t newResX, size_t newResY, uint32_t clearColor = 0, bool copyOldBits = false);
|
|
void Copy(Surface *source, EsPoint destinationPoint, EsRectangle sourceRegion, bool addToModifiedRegion);
|
|
void Draw(Surface *source, EsRectangle destinationRegion, int sourceX, int sourceY, uint16_t alpha);
|
|
void BlendWindow(Surface *source, EsPoint destinationPoint, EsRectangle sourceRegion, int material, uint8_t alpha, EsRectangle materialRegion);
|
|
void Blur(EsRectangle region, EsRectangle clip);
|
|
void SetBits(K_USER_BUFFER const void *bits, uintptr_t stride, EsRectangle region);
|
|
void Scroll(EsRectangle region, ptrdiff_t delta, bool vertical);
|
|
void CreateCursorShadow(Surface *source);
|
|
|
|
EsRectangle modifiedRegion;
|
|
};
|
|
|
|
struct Graphics {
|
|
KGraphicsTarget *target;
|
|
size_t width, height;
|
|
Surface frameBuffer;
|
|
bool debuggerActive;
|
|
size_t totalSurfaceBytes;
|
|
KMutex registerFirstGraphicsTargetMutex;
|
|
};
|
|
|
|
void GraphicsUpdateScreen(K_USER_BUFFER void *bits = nullptr, EsRectangle *bounds = nullptr, uintptr_t stride = 0);
|
|
|
|
Graphics graphics;
|
|
|
|
#else
|
|
|
|
void GraphicsUpdateScreen(K_USER_BUFFER void *bits, EsRectangle *bounds, uintptr_t bitsStride) {
|
|
KMutexAssertLocked(&windowManager.mutex);
|
|
|
|
if (windowManager.resizeWindow && windowManager.resizeStartTimeStampMs + RESIZE_FLICKER_TIMEOUT_MS > KGetTimeInMs()
|
|
&& !windowManager.inspectorWindowCount /* HACK see note in the SET_BITS syscall */) {
|
|
return;
|
|
}
|
|
|
|
if (bounds && (Width(*bounds) <= 0 || Height(*bounds) <= 0)) {
|
|
return;
|
|
}
|
|
|
|
int cursorX = windowManager.cursorX + windowManager.cursorImageOffsetX - (bounds ? bounds->l : 0);
|
|
int cursorY = windowManager.cursorY + windowManager.cursorImageOffsetY - (bounds ? bounds->t : 0);
|
|
|
|
Surface *sourceSurface;
|
|
Surface _sourceSurface;
|
|
EsRectangle _bounds;
|
|
|
|
if (bits) {
|
|
sourceSurface = &_sourceSurface;
|
|
EsMemoryZero(sourceSurface, sizeof(Surface));
|
|
sourceSurface->bits = bits;
|
|
sourceSurface->width = Width(*bounds);
|
|
sourceSurface->height = Height(*bounds);
|
|
sourceSurface->stride = bitsStride;
|
|
} else {
|
|
sourceSurface = &graphics.frameBuffer;
|
|
_bounds = ES_RECT_4(0, sourceSurface->width, 0, sourceSurface->height);
|
|
bounds = &_bounds;
|
|
}
|
|
|
|
EsRectangle cursorBounds = ES_RECT_4(cursorX, cursorX + windowManager.cursorSwap.width, cursorY, cursorY + windowManager.cursorSwap.height);
|
|
EsRectangleClip(ES_RECT_4(0, Width(*bounds), 0, Height(*bounds)), cursorBounds, &cursorBounds);
|
|
|
|
windowManager.cursorSwap.Copy(sourceSurface, ES_POINT(0, 0), cursorBounds, true);
|
|
windowManager.changedCursorImage = false;
|
|
|
|
int cursorImageWidth = windowManager.cursorSurface.width, cursorImageHeight = windowManager.cursorSurface.height;
|
|
sourceSurface->Draw(&windowManager.cursorSurface, ES_RECT_4(cursorX, cursorX + cursorImageWidth, cursorY, cursorY + cursorImageHeight), 0, 0, 0xFF);
|
|
|
|
if (bits) {
|
|
graphics.target->updateScreen((K_USER_BUFFER const uint8_t *) bits,
|
|
sourceSurface->width, sourceSurface->height,
|
|
sourceSurface->stride, bounds->l, bounds->t);
|
|
} else {
|
|
if (Width(sourceSurface->modifiedRegion) > 0 && Height(sourceSurface->modifiedRegion) > 0) {
|
|
uint8_t *bits = (uint8_t *) sourceSurface->bits
|
|
+ sourceSurface->modifiedRegion.l * 4
|
|
+ sourceSurface->modifiedRegion.t * sourceSurface->stride;
|
|
graphics.target->updateScreen(bits, Width(sourceSurface->modifiedRegion), Height(sourceSurface->modifiedRegion),
|
|
sourceSurface->width * 4, sourceSurface->modifiedRegion.l, sourceSurface->modifiedRegion.t);
|
|
sourceSurface->modifiedRegion = { (int32_t) graphics.width, 0, (int32_t) graphics.height, 0 };
|
|
}
|
|
}
|
|
|
|
sourceSurface->Copy(&windowManager.cursorSwap, ES_POINT(cursorBounds.l, cursorBounds.t), ES_RECT_4(0, Width(cursorBounds), 0, Height(cursorBounds)), true);
|
|
}
|
|
|
|
bool KGraphicsIsTargetRegistered() {
|
|
return graphics.target ? true : false;
|
|
}
|
|
|
|
void KRegisterGraphicsTarget(KGraphicsTarget *target) {
|
|
// TODO Multi-monitor support.
|
|
|
|
KMutexAcquire(&graphics.registerFirstGraphicsTargetMutex);
|
|
bool isFirst = graphics.target == nullptr;
|
|
if (isFirst) graphics.target = target;
|
|
KMutexRelease(&graphics.registerFirstGraphicsTargetMutex);
|
|
if (!isFirst) return;
|
|
|
|
KDeviceOpenHandle(target);
|
|
graphics.width = target->screenWidth;
|
|
graphics.height = target->screenHeight;
|
|
graphics.frameBuffer.Resize(graphics.width, graphics.height);
|
|
|
|
#ifdef START_DEBUG_OUTPUT
|
|
StartDebugOutput();
|
|
EsPrint("Hello\n");
|
|
#else
|
|
windowManager.Initialise();
|
|
KDeviceSendConnectedMessage(target, ES_DEVICE_GRAPHICS_TARGET);
|
|
#endif
|
|
}
|
|
|
|
bool Surface::Resize(size_t newResX, size_t newResY, uint32_t clearColor, bool copyOldBits) {
|
|
// Check the surface is within our working size limits.
|
|
if (!newResX || !newResY || newResX >= 32767 || newResY >= 32767) {
|
|
return false;
|
|
}
|
|
|
|
if (width == newResX && height == newResY) {
|
|
return true;
|
|
}
|
|
|
|
uint8_t *newBits = (uint8_t *) EsHeapAllocate(newResX * newResY * 4, !copyOldBits, K_PAGED);
|
|
|
|
if (!newBits) {
|
|
return false;
|
|
}
|
|
|
|
int oldWidth = width, oldHeight = height, oldStride = stride;
|
|
void *oldBits = bits;
|
|
|
|
width = newResX, height = newResY, bits = newBits;
|
|
stride = newResX * 4;
|
|
|
|
EsPainter painter;
|
|
painter.clip = ES_RECT_4(0, width, 0, height);
|
|
painter.target = this;
|
|
|
|
if (copyOldBits) {
|
|
EsDrawBitmap(&painter, ES_RECT_4(0, oldWidth, 0, oldHeight), (uint32_t *) oldBits, oldStride, ES_DRAW_BITMAP_OPAQUE);
|
|
|
|
if (clearColor) {
|
|
EsDrawBlock(&painter, ES_RECT_4(oldWidth, width, 0, height), clearColor);
|
|
EsDrawBlock(&painter, ES_RECT_4(0, oldWidth, oldHeight, height), clearColor);
|
|
} else {
|
|
EsDrawClear(&painter, ES_RECT_4(oldWidth, width, 0, height));
|
|
EsDrawClear(&painter, ES_RECT_4(0, oldWidth, oldHeight, height));
|
|
}
|
|
}
|
|
|
|
EsHeapFree(oldBits, 0, K_PAGED);
|
|
|
|
__sync_fetch_and_add(&graphics.totalSurfaceBytes, newResX * newResY * 4 - oldWidth * oldHeight * 4);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Surface::Copy(Surface *source, EsPoint destinationPoint, EsRectangle sourceRegion, bool addToModifiedRegion) {
|
|
EsRectangle destinationRegion = ES_RECT_4(destinationPoint.x, destinationPoint.x + Width(sourceRegion),
|
|
destinationPoint.y, destinationPoint.y + Height(sourceRegion));
|
|
|
|
if (addToModifiedRegion) {
|
|
modifiedRegion = EsRectangleBounding(destinationRegion, modifiedRegion);
|
|
EsRectangleClip(modifiedRegion, ES_RECT_4(0, width, 0, height), &modifiedRegion);
|
|
}
|
|
|
|
EsPainter painter;
|
|
painter.clip = ES_RECT_4(0, width, 0, height);
|
|
painter.target = this;
|
|
uint8_t *sourceBits = (uint8_t *) source->bits + source->stride * sourceRegion.t + 4 * sourceRegion.l;
|
|
EsDrawBitmap(&painter, destinationRegion, (uint32_t *) sourceBits, source->stride, ES_DRAW_BITMAP_OPAQUE);
|
|
}
|
|
|
|
void Surface::SetBits(K_USER_BUFFER const void *_bits, uintptr_t sourceStride, EsRectangle bounds) {
|
|
if (Width(bounds) < 0 || Height(bounds) < 0 || bounds.l < 0 || bounds.t < 0 || bounds.r > (int32_t) width || bounds.b > (int32_t) height) {
|
|
KernelPanic("Surface::SetBits - Invalid bounds %R for surface %x.\n", bounds, this);
|
|
}
|
|
|
|
if (Width(bounds) == 0 || Height(bounds) == 0) {
|
|
return;
|
|
}
|
|
|
|
modifiedRegion = EsRectangleBounding(bounds, modifiedRegion);
|
|
|
|
uint32_t *rowStart = (uint32_t *) bits + bounds.l + bounds.t * stride / 4;
|
|
K_USER_BUFFER const uint32_t *sourceRowStart = (K_USER_BUFFER const uint32_t *) _bits;
|
|
|
|
for (uintptr_t i = bounds.t; i < (uintptr_t) bounds.b; i++, rowStart += stride / 4, sourceRowStart += sourceStride / 4) {
|
|
size_t count = Width(bounds);
|
|
uint32_t *destination = rowStart;
|
|
K_USER_BUFFER const uint32_t *bits = sourceRowStart;
|
|
|
|
do {
|
|
*destination = *bits;
|
|
destination++, bits++, count--;
|
|
} while (count);
|
|
}
|
|
}
|
|
|
|
void Surface::Scroll(EsRectangle region, ptrdiff_t delta, bool vertical) {
|
|
if (vertical) {
|
|
if (delta > 0) {
|
|
for (intptr_t i = region.t; i < region.b; i++) {
|
|
for (intptr_t j = region.l; j < region.r; j++) {
|
|
((uint32_t *) bits)[j + (i - delta) * stride / 4] = ((uint32_t *) bits)[j + i * stride / 4];
|
|
}
|
|
}
|
|
} else {
|
|
for (intptr_t i = region.b - 1; i >= region.t; i--) {
|
|
for (intptr_t j = region.l; j < region.r; j++) {
|
|
((uint32_t *) bits)[j + (i - delta) * stride / 4] = ((uint32_t *) bits)[j + i * stride / 4];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (delta > 0) {
|
|
for (intptr_t i = region.t; i < region.b; i++) {
|
|
for (intptr_t j = region.l; j < region.r; j++) {
|
|
((uint32_t *) bits)[j - delta + i * stride / 4] = ((uint32_t *) bits)[j + i * stride / 4];
|
|
}
|
|
}
|
|
} else {
|
|
for (intptr_t i = region.t; i < region.b; i++) {
|
|
for (intptr_t j = region.r - 1; j >= region.l; j--) {
|
|
((uint32_t *) bits)[j - delta + i * stride / 4] = ((uint32_t *) bits)[j + i * stride / 4];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define C0(p) ((p & 0x000000FF) >> 0x00)
|
|
#define C1(p) ((p & 0x0000FF00) >> 0x08)
|
|
#define C2(p) ((p & 0x00FF0000) >> 0x10)
|
|
#define C3(p) ((p & 0xFF000000) >> 0x18)
|
|
|
|
ES_FUNCTION_OPTIMISE_O3
|
|
void BlurRegionOfImage(uint32_t *image, int width, int height, int stride, uint16_t *k, uintptr_t repeat) {
|
|
if (width <= 3 || height <= 3) {
|
|
return;
|
|
}
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
for (uintptr_t i = 0; i < repeat; i++) {
|
|
uint32_t *start = image + stride * y;
|
|
uint32_t a = start[0], b = start[0], c = start[0], d = start[0], e = start[1], f = start[2], g = 0;
|
|
uint32_t *u = start, *v = start + 3;
|
|
|
|
for (int i = 0; i < width; i++, u++, v++) {
|
|
if (i + 3 < width) g = *v;
|
|
*u = (((C0(a) * k[0] + C0(b) * k[1] + C0(c) * k[2] + C0(d) * k[3] + C0(e) * k[4] + C0(f) * k[5] + C0(g) * k[6]) >> 8) << 0x00)
|
|
+ (((C1(a) * k[0] + C1(b) * k[1] + C1(c) * k[2] + C1(d) * k[3] + C1(e) * k[4] + C1(f) * k[5] + C1(g) * k[6]) >> 8) << 0x08)
|
|
+ (((C2(a) * k[0] + C2(b) * k[1] + C2(c) * k[2] + C2(d) * k[3] + C2(e) * k[4] + C2(f) * k[5] + C2(g) * k[6]) >> 8) << 0x10)
|
|
+ (C3(d) << 0x18);
|
|
a = b, b = c, c = d, d = e, e = f, f = g;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
for (uintptr_t i = 0; i < repeat; i++) {
|
|
uint32_t *start = image + x;
|
|
uint32_t a = start[0], b = start[0], c = start[0], d = start[0], e = start[stride], f = start[stride * 2], g = 0;
|
|
uint32_t *u = start, *v = start + 3 * stride;
|
|
|
|
for (int i = 0; i < height; i++, u += stride, v += stride) {
|
|
if (i + 3 < height) g = *v;
|
|
*u = (((C0(a) * k[0] + C0(b) * k[1] + C0(c) * k[2] + C0(d) * k[3] + C0(e) * k[4] + C0(f) * k[5] + C0(g) * k[6]) >> 8) << 0x00)
|
|
+ (((C1(a) * k[0] + C1(b) * k[1] + C1(c) * k[2] + C1(d) * k[3] + C1(e) * k[4] + C1(f) * k[5] + C1(g) * k[6]) >> 8) << 0x08)
|
|
+ (((C2(a) * k[0] + C2(b) * k[1] + C2(c) * k[2] + C2(d) * k[3] + C2(e) * k[4] + C2(f) * k[5] + C2(g) * k[6]) >> 8) << 0x10)
|
|
+ (C3(d) << 0x18);
|
|
a = b, b = c, c = d, d = e, e = f, f = g;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ES_FUNCTION_OPTIMISE_O3
|
|
void BlurRegionOfImage(uint32_t *image, int width, int height, int stride, uintptr_t repeat) {
|
|
if (width <= 3 || height <= 3) {
|
|
return;
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < repeat; i++) {
|
|
uint32_t *start = image;
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
uint32_t a = start[0], b = start[0], c = start[0], d = start[0], e = start[1], f = start[2], g = 0;
|
|
uint32_t *u = start, *v = start + 3;
|
|
|
|
for (int i = 0; i < width; i++, u++, v++) {
|
|
if (i + 3 < width) g = *v;
|
|
*u = (((C0(a) * 0x07 + C0(b) * 0x1A + C0(c) * 0x38 + C0(d) * 0x4D + C0(e) * 0x38 + C0(f) * 0x1A + C0(g) * 0x07) >> 8) << 0x00)
|
|
+ (((C1(a) * 0x07 + C1(b) * 0x1A + C1(c) * 0x38 + C1(d) * 0x4D + C1(e) * 0x38 + C1(f) * 0x1A + C1(g) * 0x07) >> 8) << 0x08)
|
|
+ (((C2(a) * 0x07 + C2(b) * 0x1A + C2(c) * 0x38 + C2(d) * 0x4D + C2(e) * 0x38 + C2(f) * 0x1A + C2(g) * 0x07) >> 8) << 0x10)
|
|
+ (C3(d) << 0x18);
|
|
a = b, b = c, c = d, d = e, e = f, f = g;
|
|
}
|
|
|
|
start += stride;
|
|
}
|
|
|
|
start = image;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
uint32_t a = start[0], b = start[0], c = start[0], d = start[0], e = start[stride], f = start[stride * 2], g = 0;
|
|
uint32_t *u = start, *v = start + 3 * stride;
|
|
|
|
for (int i = 0; i < height; i++, u += stride, v += stride) {
|
|
if (i + 3 < height) g = *v;
|
|
*u = (((C0(a) * 0x07 + C0(b) * 0x1A + C0(c) * 0x38 + C0(d) * 0x4D + C0(e) * 0x38 + C0(f) * 0x1A + C0(g) * 0x07) >> 8) << 0x00)
|
|
+ (((C1(a) * 0x07 + C1(b) * 0x1A + C1(c) * 0x38 + C1(d) * 0x4D + C1(e) * 0x38 + C1(f) * 0x1A + C1(g) * 0x07) >> 8) << 0x08)
|
|
+ (((C2(a) * 0x07 + C2(b) * 0x1A + C2(c) * 0x38 + C2(d) * 0x4D + C2(e) * 0x38 + C2(f) * 0x1A + C2(g) * 0x07) >> 8) << 0x10)
|
|
+ (C3(d) << 0x18);
|
|
a = b, b = c, c = d, d = e, e = f, f = g;
|
|
}
|
|
|
|
start++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Surface::Blur(EsRectangle region, EsRectangle clip) {
|
|
#if 1
|
|
if (!EsRectangleClip(region, ES_RECT_4(0, width, 0, height), ®ion)) {
|
|
return;
|
|
}
|
|
|
|
if (!EsRectangleClip(region, clip, ®ion)) {
|
|
return;
|
|
}
|
|
|
|
BlurRegionOfImage((uint32_t *) ((uint8_t *) bits + region.l * 4 + region.t * stride), Width(region), Height(region), width, 1);
|
|
#else
|
|
EsPainter painter;
|
|
painter.clip = ES_RECT_4(0, width, 0, height);
|
|
painter.target = this;
|
|
EsDrawInvert(&painter, EsRectangleIntersection(region, clip));
|
|
#endif
|
|
}
|
|
|
|
ES_FUNCTION_OPTIMISE_O2
|
|
void Surface::BlendWindow(Surface *source, EsPoint destinationPoint, EsRectangle sourceRegion, int material, uint8_t alpha, EsRectangle materialRegion) {
|
|
if (destinationPoint.x < 0) { sourceRegion.l -= destinationPoint.x; destinationPoint.x = 0; }
|
|
if (destinationPoint.y < 0) { sourceRegion.t -= destinationPoint.y; destinationPoint.y = 0; }
|
|
if (destinationPoint.x + sourceRegion.r - sourceRegion.l >= (int) width) sourceRegion.r -= destinationPoint.x + sourceRegion.r - sourceRegion.l - width;
|
|
if (destinationPoint.y + sourceRegion.b - sourceRegion.t >= (int) height) sourceRegion.b -= destinationPoint.y + sourceRegion.b - sourceRegion.t - height;
|
|
if (sourceRegion.r > (int) source->width) sourceRegion.r = source->width;
|
|
if (sourceRegion.b > (int) source->height) sourceRegion.b = source->height;
|
|
if (sourceRegion.r <= sourceRegion.l) return;
|
|
if (sourceRegion.b <= sourceRegion.t) return;
|
|
if (sourceRegion.l < 0) return;
|
|
if (sourceRegion.t < 0) return;
|
|
|
|
EsRectangle destinationRegion = ES_RECT_4(destinationPoint.x, destinationPoint.x + Width(sourceRegion),
|
|
destinationPoint.y, destinationPoint.y + Height(sourceRegion));
|
|
modifiedRegion = EsRectangleBounding(destinationRegion, modifiedRegion);
|
|
|
|
if (material == BLEND_WINDOW_MATERIAL_GLASS || material == BLEND_WINDOW_MATERIAL_LIGHT_BLUR) {
|
|
int repeat = material == BLEND_WINDOW_MATERIAL_GLASS ? 3 : 1;
|
|
|
|
#ifndef SIMPLE_GRAPHICS
|
|
if (alpha == 0xFF) {
|
|
BlurRegionOfImage((uint32_t *) bits + materialRegion.l + materialRegion.t * width,
|
|
Width(materialRegion), Height(materialRegion), width, repeat);
|
|
} else {
|
|
uint16_t kernel[] = { 0x07, 0x1A, 0x38, 0, 0x38, 0x1A, 0x07 };
|
|
uint16_t sum = 0;
|
|
|
|
for (uintptr_t i = 0; i < 7; i++) {
|
|
kernel[i] = kernel[i] * alpha / 0xFF;
|
|
sum += kernel[i];
|
|
}
|
|
|
|
kernel[3] = 0xFF - sum;
|
|
|
|
BlurRegionOfImage((uint32_t *) bits + materialRegion.l + materialRegion.t * width,
|
|
Width(materialRegion), Height(materialRegion), width, kernel, repeat);
|
|
}
|
|
#else
|
|
(void) materialRegion;
|
|
(void) alpha;
|
|
#endif
|
|
}
|
|
|
|
intptr_t y = sourceRegion.t;
|
|
|
|
uint8_t *destinationPixel = (uint8_t *) bits + destinationPoint.y * stride + destinationPoint.x * 4;
|
|
uint8_t *sourcePixel = (uint8_t *) source->bits + sourceRegion.t * source->stride + sourceRegion.l * 4;
|
|
|
|
#ifndef SIMPLE_GRAPHICS
|
|
__m128i constantAlphaMask = _mm_set1_epi32(0xFF000000);
|
|
__m128i constantAlpha = _mm_set1_epi32(alpha);
|
|
__m128i constant255 = _mm_set1_epi32(0xFF);
|
|
#endif
|
|
|
|
while (y < sourceRegion.b) {
|
|
size_t countX = sourceRegion.r - sourceRegion.l;
|
|
|
|
uint8_t *a = destinationPixel, *b = sourcePixel;
|
|
|
|
while (countX >= 4) {
|
|
__m128i sourceValue = _mm_loadu_si128((__m128i *) sourcePixel);
|
|
|
|
#ifndef SIMPLE_GRAPHICS
|
|
__m128i destinationValue = _mm_loadu_si128((__m128i *) destinationPixel);
|
|
|
|
if (alpha != 0xFF) {
|
|
sourceValue = _mm_or_si128(_mm_andnot_si128(constantAlphaMask, sourceValue),
|
|
_mm_and_si128(_mm_slli_epi32(_mm_mullo_epi16(_mm_srli_epi32(sourceValue, 24), constantAlpha), 16), constantAlphaMask));
|
|
}
|
|
|
|
__m128i alpha = _mm_srli_epi32(sourceValue, 24);
|
|
|
|
__m128i red = _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(sourceValue, 0), constant255), alpha);
|
|
__m128i green = _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(sourceValue, 8), constant255), alpha);
|
|
__m128i blue = _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(sourceValue, 16), constant255), alpha);
|
|
|
|
alpha = _mm_sub_epi32(constant255, alpha);
|
|
|
|
red = _mm_srli_epi32(_mm_add_epi32(red, _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(destinationValue, 0), constant255), alpha)), 8);
|
|
green = _mm_srli_epi32(_mm_add_epi32(green, _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(destinationValue, 8), constant255), alpha)), 8);
|
|
blue = _mm_srli_epi32(_mm_add_epi32(blue, _mm_mullo_epi16(_mm_and_si128(_mm_srli_epi32(destinationValue, 16), constant255), alpha)), 8);
|
|
|
|
sourceValue = _mm_or_si128(_mm_slli_epi32(red, 0), _mm_or_si128(_mm_slli_epi32(green, 8), _mm_slli_epi32(blue, 16)));
|
|
#endif
|
|
|
|
_mm_storeu_si128((__m128i *) destinationPixel, sourceValue);
|
|
|
|
destinationPixel += 16;
|
|
sourcePixel += 16;
|
|
countX -= 4;
|
|
}
|
|
|
|
while (countX >= 1) {
|
|
uint32_t modified = *(uint32_t *) sourcePixel;
|
|
#ifndef SIMPLE_GRAPHICS
|
|
uint32_t original = *(uint32_t *) destinationPixel;
|
|
if (alpha != 0xFF) modified = (modified & 0xFFFFFF) | (((((modified & 0xFF000000) >> 24) * alpha) << 16) & 0xFF000000);
|
|
uint32_t m1 = (modified & 0xFF000000) >> 24;
|
|
uint32_t m2 = 255 - m1;
|
|
uint32_t r2 = m2 * (original & 0x00FF00FF);
|
|
uint32_t g2 = m2 * (original & 0x0000FF00);
|
|
uint32_t r1 = m1 * (modified & 0x00FF00FF);
|
|
uint32_t g1 = m1 * (modified & 0x0000FF00);
|
|
uint32_t result = (0x0000FF00 & ((g1 + g2) >> 8)) | (0x00FF00FF & ((r1 + r2) >> 8));
|
|
#else
|
|
uint32_t result = modified;
|
|
#endif
|
|
|
|
*(uint32_t *) destinationPixel = result;
|
|
|
|
destinationPixel += 4;
|
|
sourcePixel += 4;
|
|
countX -= 1;
|
|
}
|
|
|
|
y++;
|
|
destinationPixel = a + stride;
|
|
sourcePixel = b + source->stride;
|
|
}
|
|
}
|
|
|
|
void Surface::Draw(Surface *source, EsRectangle destinationRegion, int sourceX, int sourceY, uint16_t alpha) {
|
|
modifiedRegion = EsRectangleBounding(destinationRegion, modifiedRegion);
|
|
EsRectangleClip(modifiedRegion, ES_RECT_4(0, width, 0, height), &modifiedRegion);
|
|
EsPainter painter;
|
|
painter.clip = ES_RECT_4(0, width, 0, height);
|
|
painter.target = this;
|
|
uint8_t *sourceBits = (uint8_t *) source->bits + source->stride * sourceY + 4 * sourceX;
|
|
EsDrawBitmap(&painter, destinationRegion, (uint32_t *) sourceBits, source->stride, alpha);
|
|
}
|
|
|
|
void Surface::CreateCursorShadow(Surface *temporary) {
|
|
const uint32_t kernel[] = { 14, 43, 82, 39, 11 };
|
|
uint32_t *bits1 = (uint32_t *) bits;
|
|
uint32_t *bits2 = (uint32_t *) temporary->bits;
|
|
|
|
for (int32_t i = 0; i < (int32_t) height; i++) {
|
|
for (int32_t j = 0; j < (int32_t) width; j++) {
|
|
uint32_t s = 0;
|
|
|
|
for (int32_t k = 0; k < 5; k++) {
|
|
int32_t l = j + k - 2;
|
|
|
|
if (l >= 0 && l < (int32_t) width) {
|
|
s += ((bits1[i * stride / 4 + l] & 0xFF000000) >> 24) * kernel[k];
|
|
}
|
|
}
|
|
|
|
bits2[i * temporary->stride / 4 + j] = s;
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < (int32_t) height - CURSOR_SHADOW_OFFSET_Y; i++) {
|
|
for (int32_t j = 0; j < (int32_t) width - CURSOR_SHADOW_OFFSET_X; j++) {
|
|
uint32_t s = 0;
|
|
|
|
for (int32_t k = 0; k < 5; k++) {
|
|
int32_t l = i + k - 2;
|
|
|
|
if (l >= 0 && l < (int32_t) height) {
|
|
s += bits2[l * temporary->stride / 4 + j] * kernel[k];
|
|
}
|
|
}
|
|
|
|
uint32_t *out = &bits1[(i + CURSOR_SHADOW_OFFSET_Y) * stride / 4 + (j + CURSOR_SHADOW_OFFSET_X)];
|
|
*out = EsColorBlend((s >> 16) << 24, *out, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsUpdateScreen32(K_USER_BUFFER const uint8_t *_source, uint32_t sourceWidth, uint32_t sourceHeight, uint32_t sourceStride,
|
|
uint32_t destinationX, uint32_t destinationY,
|
|
uint32_t screenWidth, uint32_t screenHeight, uint32_t stride, volatile uint8_t *pixel) {
|
|
uint32_t *destinationRowStart = (uint32_t *) (pixel + destinationX * 4 + destinationY * stride);
|
|
const uint32_t *sourceRowStart = (const uint32_t *) _source;
|
|
|
|
if (destinationX > screenWidth || sourceWidth > screenWidth - destinationX
|
|
|| destinationY > screenHeight || sourceHeight > screenHeight - destinationY) {
|
|
KernelPanic("GraphicsUpdateScreen32 - Update region outside graphics target bounds.\n");
|
|
}
|
|
|
|
for (uintptr_t y = 0; y < sourceHeight; y++, destinationRowStart += stride / 4, sourceRowStart += sourceStride / 4) {
|
|
uint32_t *destination = destinationRowStart;
|
|
const uint32_t *source = sourceRowStart;
|
|
|
|
for (uintptr_t x = 0; x < sourceWidth; x++) {
|
|
*destination = *source;
|
|
destination++, source++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsUpdateScreen24(K_USER_BUFFER const uint8_t *_source, uint32_t sourceWidth, uint32_t sourceHeight, uint32_t sourceStride,
|
|
uint32_t destinationX, uint32_t destinationY,
|
|
uint32_t screenWidth, uint32_t screenHeight, uint32_t stride, volatile uint8_t *pixel) {
|
|
uint8_t *destinationRowStart = (uint8_t *) (pixel + destinationX * 3 + destinationY * stride);
|
|
const uint8_t *sourceRowStart = _source;
|
|
|
|
if (destinationX > screenWidth || sourceWidth > screenWidth - destinationX
|
|
|| destinationY > screenHeight || sourceHeight > screenHeight - destinationY) {
|
|
KernelPanic("GraphicsUpdateScreen32 - Update region outside graphics target bounds.\n");
|
|
}
|
|
|
|
for (uintptr_t y = 0; y < sourceHeight; y++, destinationRowStart += stride, sourceRowStart += sourceStride) {
|
|
uint8_t *destination = destinationRowStart;
|
|
const uint8_t *source = sourceRowStart;
|
|
|
|
for (uintptr_t x = 0; x < sourceWidth; x++) {
|
|
*destination++ = *source++;
|
|
*destination++ = *source++;
|
|
*destination++ = *source++;
|
|
source++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsDebugPutBlock32(uintptr_t x, uintptr_t y, bool toggle,
|
|
unsigned screenWidth, unsigned screenHeight, unsigned stride, volatile uint8_t *linearBuffer) {
|
|
(void) screenWidth;
|
|
(void) screenHeight;
|
|
|
|
if (toggle) {
|
|
linearBuffer[y * stride + x * 4 + 0] += 0x4C;
|
|
linearBuffer[y * stride + x * 4 + 1] += 0x4C;
|
|
linearBuffer[y * stride + x * 4 + 2] += 0x4C;
|
|
} else {
|
|
linearBuffer[y * stride + x * 4 + 0] = 0xFF;
|
|
linearBuffer[y * stride + x * 4 + 1] = 0xFF;
|
|
linearBuffer[y * stride + x * 4 + 2] = 0xFF;
|
|
}
|
|
|
|
linearBuffer[(y + 1) * stride + (x + 1) * 4 + 0] = 0;
|
|
linearBuffer[(y + 1) * stride + (x + 1) * 4 + 1] = 0;
|
|
linearBuffer[(y + 1) * stride + (x + 1) * 4 + 2] = 0;
|
|
}
|
|
|
|
int GraphicsDebugPutData32(const uint8_t *data, size_t dataBytes,
|
|
unsigned screenWidth, unsigned screenHeight, unsigned stride, volatile uint8_t *linearBuffer) {
|
|
uintptr_t width = (dataBytes + screenHeight * 2 - 1) / (screenHeight * 2);
|
|
uintptr_t x = screenWidth - width, y = 0;
|
|
|
|
for (uintptr_t i = 0; i < dataBytes; i += 2) {
|
|
linearBuffer[y * stride + x * 4 + 0] = data[i];
|
|
linearBuffer[y * stride + x * 4 + 1] = data[i + 1];
|
|
// Leave the red channel unmodified so we can see through to the crashed desktop state.
|
|
if (++x == screenWidth) x -= width, y++;
|
|
}
|
|
|
|
// Detection pattern.
|
|
linearBuffer[0 * stride + (screenWidth - width + 0) * 4 + 2] = 0x12;
|
|
linearBuffer[0 * stride + (screenWidth - width + 1) * 4 + 2] = 0x34;
|
|
linearBuffer[0 * stride + (screenWidth - width + 2) * 4 + 2] = 0x56;
|
|
linearBuffer[0 * stride + (screenWidth - width + 3) * 4 + 2] = 0x78;
|
|
linearBuffer[0 * stride + (screenWidth - 1 - 0) * 4 + 2] = 0x12;
|
|
linearBuffer[0 * stride + (screenWidth - 1 - 1) * 4 + 2] = 0x34;
|
|
linearBuffer[0 * stride + (screenWidth - 1 - 2) * 4 + 2] = 0x56;
|
|
linearBuffer[0 * stride + (screenWidth - 1 - 3) * 4 + 2] = 0x78;
|
|
linearBuffer[(screenHeight - 1) * stride + (screenWidth - 1 - 0) * 4 + 2] = 0x12;
|
|
linearBuffer[(screenHeight - 1) * stride + (screenWidth - 1 - 1) * 4 + 2] = 0x34;
|
|
linearBuffer[(screenHeight - 1) * stride + (screenWidth - 1 - 2) * 4 + 2] = 0x56;
|
|
linearBuffer[(screenHeight - 1) * stride + (screenWidth - 1 - 3) * 4 + 2] = 0x78;
|
|
|
|
return width;
|
|
}
|
|
|
|
void GraphicsDebugClearScreen32(unsigned screenWidth, unsigned screenHeight, unsigned stride, volatile uint8_t *linearBuffer) {
|
|
for (uintptr_t i = 0; i < screenHeight; i++) {
|
|
for (uintptr_t j = 0; j < screenWidth * 4; j += 4) {
|
|
|
|
#if 0
|
|
linearBuffer[i * stride + j + 2] = 0x18;
|
|
linearBuffer[i * stride + j + 1] = 0x7E;
|
|
linearBuffer[i * stride + j + 0] = 0xCF;
|
|
#else
|
|
if (graphics.debuggerActive) {
|
|
linearBuffer[i * stride + j + 2] = 0x18;
|
|
linearBuffer[i * stride + j + 1] = 0x7E;
|
|
linearBuffer[i * stride + j + 0] = 0xCF;
|
|
} else {
|
|
linearBuffer[i * stride + j + 2] >>= 1;
|
|
linearBuffer[i * stride + j + 1] >>= 1;
|
|
linearBuffer[i * stride + j + 0] >>= 1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef C0
|
|
#undef C1
|
|
#undef C2
|
|
#undef C3
|
|
|
|
#endif
|