| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| /* |
| * Hardware Composer Test Library |
| * Utility library functions for use by the Hardware Composer test cases |
| */ |
| |
| #include <arpa/inet.h> // For ntohl() and htonl() |
| |
| #include <cmath> |
| #include <sstream> |
| #include <string> |
| |
| #include "hwcTestLib.h" |
| |
| #include "EGLUtils.h" |
| |
| // Defines |
| #define NUMA(a) (sizeof(a) / sizeof((a)[0])) |
| |
| // Function Prototypes |
| static void printGLString(const char *name, GLenum s); |
| static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE); |
| static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config); |
| |
| using namespace std; |
| using namespace android; |
| |
| |
| #define BITSPERBYTE 8 // TODO: Obtain from <values.h>, once |
| // it has been added |
| |
| // Initialize Display |
| void hwcTestInitDisplay(bool verbose, EGLDisplay *dpy, EGLSurface *surface, |
| EGLint *width, EGLint *height) |
| { |
| static EGLContext context; |
| |
| EGLBoolean returnValue; |
| EGLConfig myConfig = {0}; |
| EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; |
| EGLint sConfigAttribs[] = { |
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| EGL_NONE }; |
| EGLint majorVersion, minorVersion; |
| |
| checkEglError("<init>"); |
| *dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| checkEglError("eglGetDisplay"); |
| if (*dpy == EGL_NO_DISPLAY) { |
| testPrintE("eglGetDisplay returned EGL_NO_DISPLAY"); |
| exit(70); |
| } |
| |
| returnValue = eglInitialize(*dpy, &majorVersion, &minorVersion); |
| checkEglError("eglInitialize", returnValue); |
| if (verbose) { |
| testPrintI("EGL version %d.%d", majorVersion, minorVersion); |
| } |
| if (returnValue != EGL_TRUE) { |
| testPrintE("eglInitialize failed"); |
| exit(71); |
| } |
| |
| // The tests want to stop the framework and play with the hardware |
| // composer, which means it doesn't make sense to use WindowSurface |
| // here. android_createDisplaySurface() is going away, so just |
| // politely fail here. |
| EGLNativeWindowType window = NULL; //android_createDisplaySurface(); |
| if (window == NULL) { |
| testPrintE("android_createDisplaySurface failed"); |
| exit(72); |
| } |
| returnValue = EGLUtils::selectConfigForNativeWindow(*dpy, |
| sConfigAttribs, window, &myConfig); |
| if (returnValue) { |
| testPrintE("EGLUtils::selectConfigForNativeWindow() returned %d", |
| returnValue); |
| exit(73); |
| } |
| checkEglError("EGLUtils::selectConfigForNativeWindow"); |
| |
| if (verbose) { |
| testPrintI("Chose this configuration:"); |
| printEGLConfiguration(*dpy, myConfig); |
| } |
| |
| *surface = eglCreateWindowSurface(*dpy, myConfig, window, NULL); |
| checkEglError("eglCreateWindowSurface"); |
| if (*surface == EGL_NO_SURFACE) { |
| testPrintE("gelCreateWindowSurface failed."); |
| exit(74); |
| } |
| |
| context = eglCreateContext(*dpy, myConfig, EGL_NO_CONTEXT, contextAttribs); |
| checkEglError("eglCreateContext"); |
| if (context == EGL_NO_CONTEXT) { |
| testPrintE("eglCreateContext failed"); |
| exit(75); |
| } |
| returnValue = eglMakeCurrent(*dpy, *surface, *surface, context); |
| checkEglError("eglMakeCurrent", returnValue); |
| if (returnValue != EGL_TRUE) { |
| testPrintE("eglMakeCurrent failed"); |
| exit(76); |
| } |
| eglQuerySurface(*dpy, *surface, EGL_WIDTH, width); |
| checkEglError("eglQuerySurface"); |
| eglQuerySurface(*dpy, *surface, EGL_HEIGHT, height); |
| checkEglError("eglQuerySurface"); |
| |
| if (verbose) { |
| testPrintI("Window dimensions: %d x %d", *width, *height); |
| |
| printGLString("Version", GL_VERSION); |
| printGLString("Vendor", GL_VENDOR); |
| printGLString("Renderer", GL_RENDERER); |
| printGLString("Extensions", GL_EXTENSIONS); |
| } |
| } |
| |
| // Open Hardware Composer Device |
| void hwcTestOpenHwc(hwc_composer_device_1_t **hwcDevicePtr) |
| { |
| int rv; |
| hw_module_t const *hwcModule; |
| |
| if ((rv = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwcModule)) != 0) { |
| testPrintE("hw_get_module failed, rv: %i", rv); |
| errno = -rv; |
| perror(NULL); |
| exit(77); |
| } |
| if ((rv = hwc_open_1(hwcModule, hwcDevicePtr)) != 0) { |
| testPrintE("hwc_open failed, rv: %i", rv); |
| errno = -rv; |
| perror(NULL); |
| exit(78); |
| } |
| } |
| |
| // Color fraction class to string conversion |
| ColorFract::operator string() |
| { |
| ostringstream out; |
| |
| out << '[' << this->c1() << ", " |
| << this->c2() << ", " |
| << this->c3() << ']'; |
| |
| return out.str(); |
| } |
| |
| // Dimension class to string conversion |
| HwcTestDim::operator string() |
| { |
| ostringstream out; |
| |
| out << '[' << this->width() << ", " |
| << this->height() << ']'; |
| |
| return out.str(); |
| } |
| |
| // Dimension class to hwc_rect conversion |
| HwcTestDim::operator hwc_rect() const |
| { |
| hwc_rect rect; |
| |
| rect.left = rect.top = 0; |
| |
| rect.right = this->_w; |
| rect.bottom = this->_h; |
| |
| return rect; |
| } |
| |
| // Hardware Composer rectangle to string conversion |
| string hwcTestRect2str(const struct hwc_rect& rect) |
| { |
| ostringstream out; |
| |
| out << '['; |
| out << rect.left << ", "; |
| out << rect.top << ", "; |
| out << rect.right << ", "; |
| out << rect.bottom; |
| out << ']'; |
| |
| return out.str(); |
| } |
| |
| // Parse HWC rectangle description of form [left, top, right, bottom] |
| struct hwc_rect hwcTestParseHwcRect(istringstream& in, bool& error) |
| { |
| struct hwc_rect rect; |
| char chStart, ch; |
| |
| // Defensively specify that an error occurred. Will clear |
| // error flag if all of parsing succeeds. |
| error = true; |
| |
| // First character should be a [ or < |
| in >> chStart; |
| if (!in || ((chStart != '<') && (chStart != '['))) { return rect; } |
| |
| // Left |
| in >> rect.left; |
| if (!in) { return rect; } |
| in >> ch; |
| if (!in || (ch != ',')) { return rect; } |
| |
| // Top |
| in >> rect.top; |
| if (!in) { return rect; } |
| in >> ch; |
| if (!in || (ch != ',')) { return rect; } |
| |
| // Right |
| in >> rect.right; |
| if (!in) { return rect; } |
| in >> ch; |
| if (!in || (ch != ',')) { return rect; } |
| |
| // Bottom |
| in >> rect.bottom; |
| if (!in) { return rect; } |
| |
| // Closing > or ] |
| in >> ch; |
| if (!in) { return rect; } |
| if (((chStart == '<') && (ch != '>')) |
| || ((chStart == '[') && (ch != ']'))) { return rect; } |
| |
| // Validate right and bottom are greater than left and top |
| if ((rect.right <= rect.left) || (rect.bottom <= rect.top)) { return rect; } |
| |
| // Made It, clear error indicator |
| error = false; |
| |
| return rect; |
| } |
| |
| // Parse dimension of form [width, height] |
| HwcTestDim hwcTestParseDim(istringstream& in, bool& error) |
| { |
| HwcTestDim dim; |
| char chStart, ch; |
| uint32_t val; |
| |
| // Defensively specify that an error occurred. Will clear |
| // error flag if all of parsing succeeds. |
| error = true; |
| |
| // First character should be a [ or < |
| in >> chStart; |
| if (!in || ((chStart != '<') && (chStart != '['))) { return dim; } |
| |
| // Width |
| in >> val; |
| if (!in) { return dim; } |
| dim.setWidth(val); |
| in >> ch; |
| if (!in || (ch != ',')) { return dim; } |
| |
| // Height |
| in >> val; |
| if (!in) { return dim; } |
| dim.setHeight(val); |
| |
| // Closing > or ] |
| in >> ch; |
| if (!in) { return dim; } |
| if (((chStart == '<') && (ch != '>')) |
| || ((chStart == '[') && (ch != ']'))) { return dim; } |
| |
| // Validate width and height greater than 0 |
| if ((dim.width() <= 0) || (dim.height() <= 0)) { return dim; } |
| |
| // Made It, clear error indicator |
| error = false; |
| return dim; |
| } |
| |
| // Parse fractional color of form [0.##, 0.##, 0.##] |
| // Fractional values can be from 0.0 to 1.0 inclusive. Note, integer |
| // values of 0.0 and 1.0, which are non-fractional, are considered valid. |
| // They are an exception, all other valid inputs are fractions. |
| ColorFract hwcTestParseColor(istringstream& in, bool& error) |
| { |
| ColorFract color; |
| char chStart, ch; |
| float c1, c2, c3; |
| |
| // Defensively specify that an error occurred. Will clear |
| // error flag if all of parsing succeeds. |
| error = true; |
| |
| // First character should be a [ or < |
| in >> chStart; |
| if (!in || ((chStart != '<') && (chStart != '['))) { return color; } |
| |
| // 1st Component |
| in >> c1; |
| if (!in) { return color; } |
| if ((c1 < 0.0) || (c1 > 1.0)) { return color; } |
| in >> ch; |
| if (!in || (ch != ',')) { return color; } |
| |
| // 2nd Component |
| in >> c2; |
| if (!in) { return color; } |
| if ((c2 < 0.0) || (c2 > 1.0)) { return color; } |
| in >> ch; |
| if (!in || (ch != ',')) { return color; } |
| |
| // 3rd Component |
| in >> c3; |
| if (!in) { return color; } |
| if ((c3 < 0.0) || (c3 > 1.0)) { return color; } |
| |
| // Closing > or ] |
| in >> ch; |
| if (!in) { return color; } |
| if (((chStart == '<') && (ch != '>')) |
| || ((chStart == '[') && (ch != ']'))) { return color; } |
| |
| // Are all the components fractional |
| if ((c1 < 0.0) || (c1 > 1.0) |
| || (c2 < 0.0) || (c2 > 1.0) |
| || (c3 < 0.0) || (c3 > 1.0)) { return color; } |
| |
| // Made It, clear error indicator |
| error = false; |
| |
| return ColorFract(c1, c2, c3); |
| } |
| |
| // Look up and return pointer to structure with the characteristics |
| // of the graphic format named by the desc parameter. Search failure |
| // indicated by the return of NULL. |
| const struct hwcTestGraphicFormat *hwcTestGraphicFormatLookup(const char *desc) |
| { |
| for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) { |
| if (string(desc) == string(hwcTestGraphicFormat[n1].desc)) { |
| return &hwcTestGraphicFormat[n1]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| // Look up and return pointer to structure with the characteristics |
| // of the graphic format specified by the id parameter. Search failure |
| // indicated by the return of NULL. |
| const struct hwcTestGraphicFormat *hwcTestGraphicFormatLookup(uint32_t id) |
| { |
| for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) { |
| if (id == hwcTestGraphicFormat[n1].format) { |
| return &hwcTestGraphicFormat[n1]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |
| // Given the integer ID of a graphic format, return a pointer to |
| // a string that describes the format. |
| const char *hwcTestGraphicFormat2str(uint32_t format) |
| { |
| const static char *unknown = "unknown"; |
| |
| for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) { |
| if (format == hwcTestGraphicFormat[n1].format) { |
| return hwcTestGraphicFormat[n1].desc; |
| } |
| } |
| |
| return unknown; |
| } |
| |
| /* |
| * hwcTestCreateLayerList |
| * Dynamically creates layer list with numLayers worth |
| * of hwLayers entries. |
| */ |
| hwc_display_contents_1_t *hwcTestCreateLayerList(size_t numLayers) |
| { |
| hwc_display_contents_1_t *list; |
| |
| size_t size = sizeof(hwc_display_contents_1_t) + numLayers * sizeof(hwc_layer_1_t); |
| if ((list = (hwc_display_contents_1_t *) calloc(1, size)) == NULL) { |
| return NULL; |
| } |
| list->flags = HWC_GEOMETRY_CHANGED; |
| list->numHwLayers = numLayers; |
| |
| return list; |
| } |
| |
| /* |
| * hwcTestFreeLayerList |
| * Frees memory previous allocated via hwcTestCreateLayerList(). |
| */ |
| void hwcTestFreeLayerList(hwc_display_contents_1_t *list) |
| { |
| free(list); |
| } |
| |
| // Display the settings of the layer list pointed to by list |
| void hwcTestDisplayList(hwc_display_contents_1_t *list) |
| { |
| testPrintI(" flags: %#x%s", list->flags, |
| (list->flags & HWC_GEOMETRY_CHANGED) ? " GEOMETRY_CHANGED" : ""); |
| testPrintI(" numHwLayers: %u", list->numHwLayers); |
| |
| for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { |
| testPrintI(" layer %u compositionType: %#x%s%s", layer, |
| list->hwLayers[layer].compositionType, |
| (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER) |
| ? " FRAMEBUFFER" : "", |
| (list->hwLayers[layer].compositionType == HWC_OVERLAY) |
| ? " OVERLAY" : ""); |
| |
| testPrintI(" hints: %#x", |
| list->hwLayers[layer].hints, |
| (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER) |
| ? " TRIPLE_BUFFER" : "", |
| (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB) |
| ? " CLEAR_FB" : ""); |
| |
| testPrintI(" flags: %#x%s", |
| list->hwLayers[layer].flags, |
| (list->hwLayers[layer].flags & HWC_SKIP_LAYER) |
| ? " SKIP_LAYER" : ""); |
| |
| testPrintI(" handle: %p", |
| list->hwLayers[layer].handle); |
| |
| // Intentionally skipped display of ROT_180 & ROT_270, |
| // which are formed from combinations of the other flags. |
| testPrintI(" transform: %#x%s%s%s", |
| list->hwLayers[layer].transform, |
| (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_H) |
| ? " FLIP_H" : "", |
| (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_V) |
| ? " FLIP_V" : "", |
| (list->hwLayers[layer].transform & HWC_TRANSFORM_ROT_90) |
| ? " ROT_90" : ""); |
| |
| testPrintI(" blending: %#x%s%s%s", |
| list->hwLayers[layer].blending, |
| (list->hwLayers[layer].blending == HWC_BLENDING_NONE) |
| ? " NONE" : "", |
| (list->hwLayers[layer].blending == HWC_BLENDING_PREMULT) |
| ? " PREMULT" : "", |
| (list->hwLayers[layer].blending == HWC_BLENDING_COVERAGE) |
| ? " COVERAGE" : ""); |
| |
| testPrintI(" sourceCrop: %s", |
| hwcTestRect2str(list->hwLayers[layer].sourceCrop).c_str()); |
| testPrintI(" displayFrame: %s", |
| hwcTestRect2str(list->hwLayers[layer].displayFrame).c_str()); |
| testPrintI(" scaleFactor: [%f, %f]", |
| (float) (list->hwLayers[layer].sourceCrop.right |
| - list->hwLayers[layer].sourceCrop.left) |
| / (float) (list->hwLayers[layer].displayFrame.right |
| - list->hwLayers[layer].displayFrame.left), |
| (float) (list->hwLayers[layer].sourceCrop.bottom |
| - list->hwLayers[layer].sourceCrop.top) |
| / (float) (list->hwLayers[layer].displayFrame.bottom |
| - list->hwLayers[layer].displayFrame.top)); |
| } |
| } |
| |
| /* |
| * Display List Prepare Modifiable |
| * |
| * Displays the portions of a list that are meant to be modified by |
| * a prepare call. |
| */ |
| void hwcTestDisplayListPrepareModifiable(hwc_display_contents_1_t *list) |
| { |
| uint32_t numOverlays = 0; |
| for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { |
| if (list->hwLayers[layer].compositionType == HWC_OVERLAY) { |
| numOverlays++; |
| } |
| testPrintI(" layer %u compositionType: %#x%s%s", layer, |
| list->hwLayers[layer].compositionType, |
| (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER) |
| ? " FRAMEBUFFER" : "", |
| (list->hwLayers[layer].compositionType == HWC_OVERLAY) |
| ? " OVERLAY" : ""); |
| testPrintI(" hints: %#x%s%s", |
| list->hwLayers[layer].hints, |
| (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER) |
| ? " TRIPLE_BUFFER" : "", |
| (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB) |
| ? " CLEAR_FB" : ""); |
| } |
| testPrintI(" numOverlays: %u", numOverlays); |
| } |
| |
| /* |
| * Display List Handles |
| * |
| * Displays the handles of all the graphic buffers in the list. |
| */ |
| void hwcTestDisplayListHandles(hwc_display_contents_1_t *list) |
| { |
| const unsigned int maxLayersPerLine = 6; |
| |
| ostringstream str(" layers:"); |
| for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { |
| str << ' ' << list->hwLayers[layer].handle; |
| if (((layer % maxLayersPerLine) == (maxLayersPerLine - 1)) |
| && (layer != list->numHwLayers - 1)) { |
| testPrintI("%s", str.str().c_str()); |
| str.str(" "); |
| } |
| } |
| testPrintI("%s", str.str().c_str()); |
| } |
| |
| // Returns a uint32_t that contains a format specific representation of a |
| // single pixel of the given color and alpha values. |
| uint32_t hwcTestColor2Pixel(uint32_t format, ColorFract color, float alpha) |
| { |
| const struct attrib { |
| uint32_t format; |
| bool hostByteOrder; |
| size_t bytes; |
| size_t c1Offset; |
| size_t c1Size; |
| size_t c2Offset; |
| size_t c2Size; |
| size_t c3Offset; |
| size_t c3Size; |
| size_t aOffset; |
| size_t aSize; |
| } attributes[] = { |
| {HAL_PIXEL_FORMAT_RGBA_8888, false, 4, 0, 8, 8, 8, 16, 8, 24, 8}, |
| {HAL_PIXEL_FORMAT_RGBX_8888, false, 4, 0, 8, 8, 8, 16, 8, 0, 0}, |
| {HAL_PIXEL_FORMAT_RGB_888, false, 3, 0, 8, 8, 8, 16, 8, 0, 0}, |
| {HAL_PIXEL_FORMAT_RGB_565, true, 2, 0, 5, 5, 6, 11, 5, 0, 0}, |
| {HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8, 8, 8, 0, 8, 24, 8}, |
| {HAL_PIXEL_FORMAT_YV12, true, 3, 16, 8, 8, 8, 0, 8, 0, 0}, |
| }; |
| |
| const struct attrib *attrib; |
| for (attrib = attributes; attrib < attributes + NUMA(attributes); |
| attrib++) { |
| if (attrib->format == format) { break; } |
| } |
| if (attrib >= attributes + NUMA(attributes)) { |
| testPrintE("colorFract2Pixel unsupported format of: %u", format); |
| exit(80); |
| } |
| |
| uint32_t pixel; |
| pixel = htonl((uint32_t) round((((1 << attrib->c1Size) - 1) * color.c1())) |
| << ((sizeof(pixel) * BITSPERBYTE) |
| - (attrib->c1Offset + attrib->c1Size))); |
| pixel |= htonl((uint32_t) round((((1 << attrib->c2Size) - 1) * color.c2())) |
| << ((sizeof(pixel) * BITSPERBYTE) |
| - (attrib->c2Offset + attrib->c2Size))); |
| pixel |= htonl((uint32_t) round((((1 << attrib->c3Size) - 1) * color.c3())) |
| << ((sizeof(pixel) * BITSPERBYTE) |
| - (attrib->c3Offset + attrib->c3Size))); |
| if (attrib->aSize) { |
| pixel |= htonl((uint32_t) round((((1 << attrib->aSize) - 1) * alpha)) |
| << ((sizeof(pixel) * BITSPERBYTE) |
| - (attrib->aOffset + attrib->aSize))); |
| } |
| if (attrib->hostByteOrder) { |
| pixel = ntohl(pixel); |
| pixel >>= sizeof(pixel) * BITSPERBYTE - attrib->bytes * BITSPERBYTE; |
| } |
| |
| return pixel; |
| } |
| |
| // Sets the pixel at the given x and y coordinates to the color and alpha |
| // value given by pixel. The contents of pixel is format specific. It's |
| // value should come from a call to hwcTestColor2Pixel(). |
| void hwcTestSetPixel(GraphicBuffer *gBuf, unsigned char *buf, |
| uint32_t x, uint32_t y, uint32_t pixel) |
| { |
| |
| const struct attrib { |
| int format; |
| size_t bytes; |
| } attributes[] = { |
| {HAL_PIXEL_FORMAT_RGBA_8888, 4}, |
| {HAL_PIXEL_FORMAT_RGBX_8888, 4}, |
| {HAL_PIXEL_FORMAT_RGB_888, 3}, |
| {HAL_PIXEL_FORMAT_RGB_565, 2}, |
| {HAL_PIXEL_FORMAT_BGRA_8888, 4}, |
| }; |
| |
| if (gBuf->getPixelFormat() == HAL_PIXEL_FORMAT_YV12) { |
| uint32_t yPlaneOffset, uPlaneOffset, vPlaneOffset; |
| uint32_t yPlaneStride = gBuf->getStride(); |
| uint32_t uPlaneStride = ((gBuf->getStride() / 2) + 0xf) & ~0xf; |
| uint32_t vPlaneStride = uPlaneStride; |
| yPlaneOffset = 0; |
| vPlaneOffset = yPlaneOffset + yPlaneStride * gBuf->getHeight(); |
| uPlaneOffset = vPlaneOffset |
| + vPlaneStride * (gBuf->getHeight() / 2); |
| *(buf + yPlaneOffset + y * yPlaneStride + x) = pixel & 0xff; |
| *(buf + uPlaneOffset + (y / 2) * uPlaneStride + (x / 2)) |
| = (pixel & 0xff00) >> 8; |
| *(buf + vPlaneOffset + (y / 2) * vPlaneStride + (x / 2)) |
| = (pixel & 0xff0000) >> 16; |
| |
| return; |
| } |
| |
| const struct attrib *attrib; |
| for (attrib = attributes; attrib < attributes + NUMA(attributes); |
| attrib++) { |
| if (attrib->format == gBuf->getPixelFormat()) { break; } |
| } |
| if (attrib >= attributes + NUMA(attributes)) { |
| testPrintE("setPixel unsupported format of: %u", |
| gBuf->getPixelFormat()); |
| exit(90); |
| } |
| |
| memmove(buf + ((gBuf->getStride() * attrib->bytes) * y) |
| + (attrib->bytes * x), &pixel, attrib->bytes); |
| } |
| |
| // Fill a given graphic buffer with a uniform color and alpha |
| void hwcTestFillColor(GraphicBuffer *gBuf, ColorFract color, float alpha) |
| { |
| unsigned char* buf = NULL; |
| status_t err; |
| uint32_t pixel; |
| |
| pixel = hwcTestColor2Pixel(gBuf->getPixelFormat(), color, alpha); |
| |
| err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); |
| if (err != 0) { |
| testPrintE("hwcTestFillColor lock failed: %d", err); |
| exit(100); |
| } |
| |
| for (unsigned int x = 0; x < gBuf->getStride(); x++) { |
| for (unsigned int y = 0; y < gBuf->getHeight(); y++) { |
| hwcTestSetPixel(gBuf, buf, x, y, (x < gBuf->getWidth()) |
| ? pixel : testRand()); |
| } |
| } |
| |
| err = gBuf->unlock(); |
| if (err != 0) { |
| testPrintE("hwcTestFillColor unlock failed: %d", err); |
| exit(101); |
| } |
| } |
| |
| // Fill the given buffer with a horizontal blend of colors, with the left |
| // side color given by startColor and the right side color given by |
| // endColor. The startColor and endColor values are specified in the format |
| // given by colorFormat, which might be different from the format of the |
| // graphic buffer. When different, a color conversion is done when possible |
| // to the graphic format of the graphic buffer. A color of black is |
| // produced for cases where the conversion is impossible (e.g. out of gamut |
| // values). |
| void hwcTestFillColorHBlend(GraphicBuffer *gBuf, uint32_t colorFormat, |
| ColorFract startColor, ColorFract endColor) |
| { |
| status_t err; |
| unsigned char* buf = NULL; |
| const uint32_t width = gBuf->getWidth(); |
| const uint32_t height = gBuf->getHeight(); |
| const uint32_t stride = gBuf->getStride(); |
| |
| err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); |
| if (err != 0) { |
| testPrintE("hwcTestFillColorHBlend lock failed: %d", err); |
| exit(110); |
| } |
| |
| for (unsigned int x = 0; x < stride; x++) { |
| uint32_t pixel; |
| if (x < width) { |
| ColorFract color(startColor.c1() + (endColor.c1() - startColor.c1()) |
| * ((float) x / (float) (width - 1)), |
| startColor.c2() + (endColor.c2() - startColor.c2()) |
| * ((float) x / (float) (width - 1)), |
| startColor.c3() + (endColor.c3() - startColor.c3()) |
| * ((float) x / (float) (width - 1))); |
| |
| // When formats differ, convert colors. |
| // Important to not convert when formats are the same, since |
| // out of gamut colors are always converted to black. |
| if (colorFormat != (uint32_t) gBuf->getPixelFormat()) { |
| hwcTestColorConvert(colorFormat, gBuf->getPixelFormat(), color); |
| } |
| pixel = hwcTestColor2Pixel(gBuf->getPixelFormat(), color, 1.0); |
| } else { |
| // Fill pad with random values |
| pixel = testRand(); |
| } |
| |
| for (unsigned int y = 0; y < height; y++) { |
| hwcTestSetPixel(gBuf, buf, x, y, pixel); |
| } |
| } |
| |
| err = gBuf->unlock(); |
| if (err != 0) { |
| testPrintE("hwcTestFillColorHBlend unlock failed: %d", err); |
| exit(111); |
| } |
| } |
| |
| /* |
| * When possible, converts color specified as a full range value in |
| * the fromFormat, into an equivalent full range color in the toFormat. |
| * When conversion is impossible (e.g. out of gamut color) a color |
| * or black in the full range output format is produced. The input |
| * color is given as a fractional color in the parameter named color. |
| * The produced color is written over the same parameter used to |
| * provide the input color. |
| * |
| * Each graphic format has 3 color components and each of these |
| * components has both a full and in gamut range. This function uses |
| * a table that provides the full and in gamut ranges of each of the |
| * supported graphic formats. The full range is given by members named |
| * c[123]Min to c[123]Max, while the in gamut range is given by members |
| * named c[123]Low to c[123]High. In most cases the full and in gamut |
| * ranges are equivalent. This occurs when the c[123]Min == c[123]Low and |
| * c[123]High == c[123]Max. |
| * |
| * The input and produced colors are both specified as a fractional amount |
| * of the full range. The diagram below provides an overview of the |
| * conversion process. The main steps are: |
| * |
| * 1. Produce black if the input color is out of gamut. |
| * |
| * 2. Convert the in gamut color into the fraction of the fromFromat |
| * in gamut range. |
| * |
| * 3. Convert from the fraction of the in gamut from format range to |
| * the fraction of the in gamut to format range. Produce black |
| * if an equivalent color does not exists. |
| * |
| * 4. Covert from the fraction of the in gamut to format to the |
| * fraction of the full range to format. |
| * |
| * From Format To Format |
| * max high high max |
| * ----+ +-----------+ |
| * high \ / \ high |
| * ------\-------------+ +--------> |
| * \ |
| * \ +--- black --+ |
| * \ / \ |
| * \ / +--> |
| * low \ / low |
| * -------- ---+-- black --+ |
| * min low low min |
| * ^ ^ ^ ^ ^ |
| * | | | | | |
| * | | | | +-- fraction of full range |
| * | | | +-- fraction of valid range |
| * | | +-- fromFormat to toFormat color conversion |
| * | +-- fraction of valid range |
| * +-- fraction of full range |
| */ |
| void hwcTestColorConvert(uint32_t fromFormat, uint32_t toFormat, |
| ColorFract& color) |
| { |
| const struct attrib { |
| uint32_t format; |
| bool rgb; |
| bool yuv; |
| int c1Min, c1Low, c1High, c1Max; |
| int c2Min, c2Low, c2High, c2Max; |
| int c3Min, c3Low, c3High, c3Max; |
| } attributes[] = { |
| {HAL_PIXEL_FORMAT_RGBA_8888, true, false, |
| 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255}, |
| {HAL_PIXEL_FORMAT_RGBX_8888, true, false, |
| 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255}, |
| {HAL_PIXEL_FORMAT_RGB_888, true, false, |
| 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255}, |
| {HAL_PIXEL_FORMAT_RGB_565, true, false, |
| 0, 0, 31, 31, 0, 0, 63, 63, 0, 0, 31, 31}, |
| {HAL_PIXEL_FORMAT_BGRA_8888, true, false, |
| 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255}, |
| {HAL_PIXEL_FORMAT_YV12, false, true, |
| 0, 16, 235, 255, 0, 16, 240, 255, 0, 16, 240, 255}, |
| }; |
| |
| const struct attrib *fromAttrib; |
| for (fromAttrib = attributes; fromAttrib < attributes + NUMA(attributes); |
| fromAttrib++) { |
| if (fromAttrib->format == fromFormat) { break; } |
| } |
| if (fromAttrib >= attributes + NUMA(attributes)) { |
| testPrintE("hwcTestColorConvert unsupported from format of: %u", |
| fromFormat); |
| exit(120); |
| } |
| |
| const struct attrib *toAttrib; |
| for (toAttrib = attributes; toAttrib < attributes + NUMA(attributes); |
| toAttrib++) { |
| if (toAttrib->format == toFormat) { break; } |
| } |
| if (toAttrib >= attributes + NUMA(attributes)) { |
| testPrintE("hwcTestColorConvert unsupported to format of: %u", |
| toFormat); |
| exit(121); |
| } |
| |
| // Produce black if any of the from components are outside the |
| // valid color range |
| float c1Val = fromAttrib->c1Min |
| + ((float) (fromAttrib->c1Max - fromAttrib->c1Min) * color.c1()); |
| float c2Val = fromAttrib->c2Min |
| + ((float) (fromAttrib->c2Max - fromAttrib->c2Min) * color.c2()); |
| float c3Val = fromAttrib->c3Min |
| + ((float) (fromAttrib->c3Max - fromAttrib->c3Min) * color.c3()); |
| if ((c1Val < fromAttrib->c1Low) || (c1Val > fromAttrib->c1High) |
| || (c2Val < fromAttrib->c2Low) || (c2Val > fromAttrib->c2High) |
| || (c3Val < fromAttrib->c3Low) || (c3Val > fromAttrib->c3High)) { |
| |
| // Return black |
| // Will use representation of black from RGBA8888 graphic format |
| // and recursively convert it to the requested graphic format. |
| color = ColorFract(0.0, 0.0, 0.0); |
| hwcTestColorConvert(HAL_PIXEL_FORMAT_RGBA_8888, toFormat, color); |
| return; |
| } |
| |
| // Within from format, convert from fraction of full range |
| // to fraction of valid range |
| color = ColorFract((c1Val - fromAttrib->c1Low) |
| / (fromAttrib->c1High - fromAttrib->c1Low), |
| (c2Val - fromAttrib->c2Low) |
| / (fromAttrib->c2High - fromAttrib->c2Low), |
| (c3Val - fromAttrib->c3Low) |
| / (fromAttrib->c3High - fromAttrib->c3Low)); |
| |
| // If needed perform RGB to YUV conversion |
| float wr = 0.2126, wg = 0.7152, wb = 0.0722; // ITU709 recommended constants |
| if (fromAttrib->rgb && toAttrib->yuv) { |
| float r = color.c1(), g = color.c2(), b = color.c3(); |
| float y = wr * r + wg * g + wb * b; |
| float u = 0.5 * ((b - y) / (1.0 - wb)) + 0.5; |
| float v = 0.5 * ((r - y) / (1.0 - wr)) + 0.5; |
| |
| // Produce black if color is outside the YUV gamut |
| if ((y < 0.0) || (y > 1.0) |
| || (u < 0.0) || (u > 1.0) |
| || (v < 0.0) || (v > 1.0)) { |
| y = 0.0; |
| u = v = 0.5; |
| } |
| |
| color = ColorFract(y, u, v); |
| } |
| |
| // If needed perform YUV to RGB conversion |
| // Equations determined from the ITU709 equations for RGB to YUV |
| // conversion, plus the following algebra: |
| // |
| // u = 0.5 * ((b - y) / (1.0 - wb)) + 0.5 |
| // 0.5 * ((b - y) / (1.0 - wb)) = u - 0.5 |
| // (b - y) / (1.0 - wb) = 2 * (u - 0.5) |
| // b - y = 2 * (u - 0.5) * (1.0 - wb) |
| // b = 2 * (u - 0.5) * (1.0 - wb) + y |
| // |
| // v = 0.5 * ((r -y) / (1.0 - wr)) + 0.5 |
| // 0.5 * ((r - y) / (1.0 - wr)) = v - 0.5 |
| // (r - y) / (1.0 - wr) = 2 * (v - 0.5) |
| // r - y = 2 * (v - 0.5) * (1.0 - wr) |
| // r = 2 * (v - 0.5) * (1.0 - wr) + y |
| // |
| // y = wr * r + wg * g + wb * b |
| // wr * r + wg * g + wb * b = y |
| // wg * g = y - wr * r - wb * b |
| // g = (y - wr * r - wb * b) / wg |
| if (fromAttrib->yuv && toAttrib->rgb) { |
| float y = color.c1(), u = color.c2(), v = color.c3(); |
| float r = 2.0 * (v - 0.5) * (1.0 - wr) + y; |
| float b = 2.0 * (u - 0.5) * (1.0 - wb) + y; |
| float g = (y - wr * r - wb * b) / wg; |
| |
| // Produce black if color is outside the RGB gamut |
| if ((r < 0.0) || (r > 1.0) |
| || (g < 0.0) || (g > 1.0) |
| || (b < 0.0) || (b > 1.0)) { |
| r = g = b = 0.0; |
| } |
| |
| color = ColorFract(r, g, b); |
| } |
| |
| // Within to format, convert from fraction of valid range |
| // to fraction of full range |
| c1Val = (toAttrib->c1Low |
| + (float) (toAttrib->c1High - toAttrib->c1Low) * color.c1()); |
| c2Val = (toAttrib->c1Low |
| + (float) (toAttrib->c2High - toAttrib->c2Low) * color.c2()); |
| c3Val = (toAttrib->c1Low |
| + (float) (toAttrib->c3High - toAttrib->c3Low) * color.c3()); |
| color = ColorFract((float) (c1Val - toAttrib->c1Min) |
| / (float) (toAttrib->c1Max - toAttrib->c1Min), |
| (float) (c2Val - toAttrib->c2Min) |
| / (float) (toAttrib->c2Max - toAttrib->c2Min), |
| (float) (c3Val - toAttrib->c3Min) |
| / (float) (toAttrib->c3Max - toAttrib->c3Min)); |
| } |
| |
| // TODO: Use PrintGLString, CechckGlError, and PrintEGLConfiguration |
| // from libglTest |
| static void printGLString(const char *name, GLenum s) |
| { |
| const char *v = (const char *) glGetString(s); |
| |
| if (v == NULL) { |
| testPrintI("GL %s unknown", name); |
| } else { |
| testPrintI("GL %s = %s", name, v); |
| } |
| } |
| |
| static void checkEglError(const char* op, EGLBoolean returnVal) |
| { |
| if (returnVal != EGL_TRUE) { |
| testPrintE("%s() returned %d", op, returnVal); |
| } |
| |
| for (EGLint error = eglGetError(); error != EGL_SUCCESS; error |
| = eglGetError()) { |
| testPrintE("after %s() eglError %s (0x%x)", |
| op, EGLUtils::strerror(error), error); |
| } |
| } |
| |
| static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) |
| { |
| |
| #define X(VAL) {VAL, #VAL} |
| struct {EGLint attribute; const char* name;} names[] = { |
| X(EGL_BUFFER_SIZE), |
| X(EGL_ALPHA_SIZE), |
| X(EGL_BLUE_SIZE), |
| X(EGL_GREEN_SIZE), |
| X(EGL_RED_SIZE), |
| X(EGL_DEPTH_SIZE), |
| X(EGL_STENCIL_SIZE), |
| X(EGL_CONFIG_CAVEAT), |
| X(EGL_CONFIG_ID), |
| X(EGL_LEVEL), |
| X(EGL_MAX_PBUFFER_HEIGHT), |
| X(EGL_MAX_PBUFFER_PIXELS), |
| X(EGL_MAX_PBUFFER_WIDTH), |
| X(EGL_NATIVE_RENDERABLE), |
| X(EGL_NATIVE_VISUAL_ID), |
| X(EGL_NATIVE_VISUAL_TYPE), |
| X(EGL_SAMPLES), |
| X(EGL_SAMPLE_BUFFERS), |
| X(EGL_SURFACE_TYPE), |
| X(EGL_TRANSPARENT_TYPE), |
| X(EGL_TRANSPARENT_RED_VALUE), |
| X(EGL_TRANSPARENT_GREEN_VALUE), |
| X(EGL_TRANSPARENT_BLUE_VALUE), |
| X(EGL_BIND_TO_TEXTURE_RGB), |
| X(EGL_BIND_TO_TEXTURE_RGBA), |
| X(EGL_MIN_SWAP_INTERVAL), |
| X(EGL_MAX_SWAP_INTERVAL), |
| X(EGL_LUMINANCE_SIZE), |
| X(EGL_ALPHA_MASK_SIZE), |
| X(EGL_COLOR_BUFFER_TYPE), |
| X(EGL_RENDERABLE_TYPE), |
| X(EGL_CONFORMANT), |
| }; |
| #undef X |
| |
| for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { |
| EGLint value = -1; |
| EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, |
| &value); |
| EGLint error = eglGetError(); |
| if (returnVal && error == EGL_SUCCESS) { |
| testPrintI(" %s: %d (%#x)", names[j].name, value, value); |
| } |
| } |
| testPrintI(""); |
| } |