From 4ae63ae816e2f8792b7c018853e086fd140769b8 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 18 Aug 2011 11:53:30 -0700 Subject: Add ILK+ sprite support Use the new DRM plane code to support overlay sprites on ILK+. Signed-off-by: Jesse Barnes --- src/Makefile.am | 1 + src/intel.h | 3 + src/intel_display.c | 53 +++++++ src/intel_sprite.c | 439 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/intel_video.c | 17 +- src/intel_video.h | 32 ++++ 6 files changed, 540 insertions(+), 5 deletions(-) create mode 100644 src/intel_sprite.c diff --git a/src/Makefile.am b/src/Makefile.am index cd1bb36e..b092a8c8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,7 @@ intel_drv_la_SOURCES = \ i965_3d.c \ i965_video.c \ i965_render.c \ + intel_sprite.c \ $(NULL) if DRI diff --git a/src/intel.h b/src/intel.h index 42afaf47..b1ed7da3 100644 --- a/src/intel.h +++ b/src/intel.h @@ -334,6 +334,7 @@ typedef struct intel_screen_private { int colorKey; XF86VideoAdaptorPtr adaptor; + XF86VideoAdaptorPtr sprite_adaptor; ScreenBlockHandlerProcPtr BlockHandler; Bool overlayOn; @@ -464,6 +465,7 @@ extern void intel_mode_fini(intel_screen_private *intel); extern int intel_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc); extern int intel_crtc_id(xf86CrtcPtr crtc); +extern int intel_crtc_to_sprite(xf86CrtcPtr crtc); extern int intel_output_dpms_status(xf86OutputPtr output); enum DRI2FrameEventType { @@ -528,6 +530,7 @@ extern void I915EmitInvarientState(ScrnInfoPtr scrn); extern void I830EmitFlush(ScrnInfoPtr scrn); extern void I830InitVideo(ScreenPtr pScreen); +extern XF86VideoAdaptorPtr intel_setup_sprite(ScreenPtr screen); extern xf86CrtcPtr intel_covering_crtc(ScrnInfoPtr scrn, BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret); diff --git a/src/intel_display.c b/src/intel_display.c index 84c7c08c..d75a6070 100644 --- a/src/intel_display.c +++ b/src/intel_display.c @@ -75,6 +75,7 @@ struct intel_crtc { dri_bo *rotate_bo; uint32_t rotate_pitch; uint32_t rotate_fb_id; + uint32_t sprite_id; xf86CrtcPtr crtc; struct list link; }; @@ -1576,9 +1577,50 @@ drm_wakeup_handler(pointer data, int err, pointer p) drmHandleEvent(mode->fd, &mode->event_context); } +static void intel_crtc_find_plane(ScrnInfoPtr scrn, xf86CrtcPtr crtc) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_crtc *intel_crtc = crtc->driver_private; + drmModePlaneRes *plane_resources; + drmModePlane *ovr; + uint32_t id = 0; + int i; + + plane_resources = drmModeGetPlaneResources(intel->drmSubFD); + if (!plane_resources) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to get plane resources: %s\n", + strerror(errno)); + goto out; + } + + for (i = 0; i < plane_resources->count_planes; i++) { + ovr = drmModeGetPlane(intel->drmSubFD, + plane_resources->planes[i]); + if (!ovr) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to get plane info: %s\n", + strerror(errno)); + continue; + } + + if (ovr->possible_crtcs & (1 << intel_crtc_to_pipe(crtc))) { + id = ovr->plane_id; + drmModeFreePlane(ovr); + break; + } + drmModeFreePlane(ovr); + } + + free(plane_resources); +out: + intel_crtc->sprite_id = id; +} + Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) { intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_crtc *intel_crtc; struct drm_i915_getparam gp; struct intel_mode *mode; unsigned int i; @@ -1609,6 +1651,9 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) for (i = 0; i < mode->mode_res->count_crtcs; i++) intel_crtc_init(scrn, mode, i); + list_for_each_entry(intel_crtc, &mode->crtcs, link) + intel_crtc_find_plane(scrn, intel_crtc->crtc); + for (i = 0; i < mode->mode_res->count_connectors; i++) intel_output_init(scrn, mode, i); @@ -1630,6 +1675,8 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) } intel->modes = mode; + + return TRUE; } @@ -1699,3 +1746,9 @@ int intel_crtc_to_pipe(xf86CrtcPtr crtc) struct intel_crtc *intel_crtc = crtc->driver_private; return intel_crtc->pipe; } + +int intel_crtc_to_sprite(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + return intel_crtc->sprite_id; +} diff --git a/src/intel_sprite.c b/src/intel_sprite.c new file mode 100644 index 00000000..c6fe7579 --- /dev/null +++ b/src/intel_sprite.c @@ -0,0 +1,439 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "xf86.h" +#include "xf86_OSproc.h" +#include "compiler.h" +#include "xf86PciInfo.h" +#include "xf86Pci.h" +#include "xf86fbman.h" +#include "regionstr.h" +#include "randrstr.h" +#include "windowstr.h" +#include "damage.h" +#include "intel.h" +#include "intel_video.h" +#include "i830_reg.h" +#include "xf86xv.h" +#include +#include "dixstruct.h" +#include "fourcc.h" + +#define IMAGE_MAX_WIDTH 2048 +#define IMAGE_MAX_HEIGHT 2048 + +#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE) + +static Atom xvColorKey; + +static XF86VideoFormatRec xv_formats[] = { + {15, TrueColor}, {16, TrueColor}, {24, TrueColor} +}; + +static XF86ImageRec xv_images[] = { + XVIMAGE_YUY2, + XVIMAGE_UYVY, +}; + +static const XF86VideoEncodingRec xv_dummy_encoding[] = { + { + 0, + "XV_IMAGE", + IMAGE_MAX_WIDTH, IMAGE_MAX_HEIGHT, + {1, 1} + } +}; + +static XF86AttributeRec attribs[] = { + {XvSettable | XvGettable, 0, 0xffffff, "XV_COLORKEY"}, +}; + +static void intel_sprite_stop(ScrnInfoPtr scrn, pointer data, Bool shutdown) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + intel_adaptor_private *adaptor_priv = intel_get_sprite_adaptor_private(intel); + int ret; + + ret = drmModeSetPlane(intel->drmSubFD, adaptor_priv->plane_id, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0); + if (ret) + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to disable plane\n"); +} + +static int intel_sprite_set_attr(ScrnInfoPtr scrn, Atom attribute, INT32 value, + pointer data) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + intel_adaptor_private *adaptor_priv = intel_get_sprite_adaptor_private(intel); + + if (attribute == xvColorKey) { + adaptor_priv->colorKeyChanged = TRUE; + adaptor_priv->colorKey = value; + ErrorF("COLORKEY = %d\n", value); + } else + return BadMatch; + + return Success; +} + +static int intel_sprite_get_attr(ScrnInfoPtr scrn, Atom attribute, INT32 *value, + pointer data) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + intel_adaptor_private *adaptor_priv = intel_get_sprite_adaptor_private(intel); + + if (attribute == xvColorKey) + *value = adaptor_priv->colorKey; + else + return BadMatch; + + return Success; +} + +static void intel_sprite_best_size(ScrnInfoPtr scrn, Bool motion, short vid_w, + short vid_h, short drw_w, short drw_h, + unsigned int *p_w, unsigned int *p_h, + pointer data) +{ + *p_w = vid_w; + *p_h = vid_h; +} + +static Bool intel_plane_moved(intel_adaptor_private *adaptor_priv, + BoxRec *new_location) +{ + uint32_t new_w = new_location->x2 - new_location->x1; + uint32_t new_h = new_location->y2 - new_location->y1; + + if (adaptor_priv->crtc_x != new_location->x1 || + adaptor_priv->crtc_y != new_location->y1 || + adaptor_priv->crtc_w != new_w || + adaptor_priv->crtc_h != new_h) + return TRUE; + return FALSE; +} + +static Bool intel_source_changed(intel_adaptor_private *adaptor_priv, + int src_x, int src_y, int src_w, int src_h) +{ + if (adaptor_priv->src_x != src_x || + adaptor_priv->src_y != src_y || + adaptor_priv->src_w != src_w || + adaptor_priv->src_h != src_h) + return TRUE; + return FALSE; +} + +static int intel_sprite_put(ScrnInfoPtr scrn, short src_x, short src_y, + short drw_x, short drw_y, short src_w, short src_h, + short drw_w, short drw_h, int id, + unsigned char *buf, short width, short height, + Bool sync, RegionPtr clipBoxes, pointer data, + DrawablePtr drawable) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + intel_adaptor_private *adaptor_priv = intel_get_sprite_adaptor_private(intel); + dri_bo *new_frame; + unsigned char *src, *dst, *dst_base; + int i; + xf86CrtcPtr crtc; + BoxRec dst_box; + int ret, top, left, lines, pixel_width; + int crtc_x, crtc_y; + unsigned long crtc_w, crtc_h; + int plane_id; + uint32_t fb_id, crtc_id; + Bool plane_updated = FALSE; + uint32_t pixel_format; + uint32_t pitches[4], offsets[4]; + + ret = intel_clip_video_helper(scrn, adaptor_priv, &crtc, &dst_box, + src_x, src_y, drw_x, drw_y, + src_w, src_h, drw_w, drw_h, + id, &top, &left, &pixel_width, &lines, + clipBoxes, width, height); + if (!ret) + return Success; + + crtc_id = intel_crtc_id(crtc); + + /* Make output coords CRTC relative */ + crtc_x = dst_box.x1 - crtc->x; + crtc_y = dst_box.y1 - crtc->y; + crtc_w = dst_box.x2 - dst_box.x1; + crtc_h = dst_box.y2 - dst_box.y1; + + plane_id = intel_crtc_to_sprite(crtc); + if (!plane_id) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "no planes available for crtc\n"); + return Success; + } + + new_frame = adaptor_priv->buf; + + if (adaptor_priv->plane_id != plane_id || + adaptor_priv->colorKeyChanged) { + struct drm_intel_set_sprite_destkey set; + + set.plane_id = plane_id; + set.value = adaptor_priv->colorKey; + ret = drmCommandWrite(intel->drmSubFD, + DRM_I915_SET_SPRITE_DESTKEY, &set, + sizeof(set)); + if (ret) + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to update color key\n"); + adaptor_priv->colorKeyChanged = FALSE; + } + + /* Re-alloc if source size changed */ + if (new_frame && + intel_source_changed(adaptor_priv, top, left, pixel_width, lines)) { + drm_intel_bo_unreference(new_frame); + new_frame = NULL; + } + + if (!new_frame) { + uint32_t tiling_mode = I915_TILING_X; + unsigned long alloc_pitch; + + memset(offsets, 0, sizeof(offsets)); + + switch (id) { + case FOURCC_UYVY: + pixel_format = V4L2_PIX_FMT_UYVY; + break; + case FOURCC_YUY2: + default: + pixel_format = V4L2_PIX_FMT_YUYV; + break; + } + + new_frame = drm_intel_bo_alloc_tiled(intel->bufmgr, + "sprite buffer", + pixel_width, lines, + drawable->bitsPerPixel / 8, + &tiling_mode, &alloc_pitch, + BO_ALLOC_FOR_RENDER); + if (!new_frame) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to create bo for video data: %s\n", + strerror(errno)); + return BadAlloc; + } + adaptor_priv->reusable = TRUE; + adaptor_priv->buf = new_frame; + adaptor_priv->pitch = alloc_pitch; + ErrorF("allocated new frame, %dx%d, pitch %ld\n", pixel_width, + lines, alloc_pitch); + + pitches[0] = adaptor_priv->pitch; + + ret = drmModeAddFB2(intel->drmSubFD, pixel_width, lines, + pixel_format, new_frame->handle, + pitches, offsets, &fb_id); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to add fb (depth %d, bpp %d) " + "for video data: %s\n", + drawable->depth, drawable->bitsPerPixel, + strerror(errno)); + return BadAlloc; + } + adaptor_priv->fb_id = fb_id; + plane_updated = TRUE; + } + + if (intel_plane_moved(adaptor_priv, &dst_box) || + intel_source_changed(adaptor_priv, top, left, pixel_width, lines) || + adaptor_priv->crtc_id != crtc_id || + adaptor_priv->plane_id != plane_id) { + plane_updated = TRUE; + adaptor_priv->crtc_x = crtc_x; + adaptor_priv->crtc_y = crtc_y; + adaptor_priv->crtc_w = crtc_w; + adaptor_priv->crtc_h = crtc_h; + adaptor_priv->src_x = top; + adaptor_priv->src_y = left; + adaptor_priv->src_w = pixel_width; + adaptor_priv->src_h = lines; + /* Must have switched CRTCs, disable the old one */ + if (plane_id != adaptor_priv->plane_id) + drmModeSetPlane(intel->drmSubFD, adaptor_priv->plane_id, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + adaptor_priv->crtc_id = crtc_id; + adaptor_priv->plane_id = plane_id; + } + + if (drm_intel_gem_bo_map_gtt(new_frame)) + return BadAlloc; + + dst_base = new_frame->virtual; + dst = dst_base + adaptor_priv->YBufOffset; + + src = buf + (top * (width * 2)) + (left << 1); + + for (i = 0; i < height; i++) { + memcpy(dst, src, width * 2); + src += width * 2; + dst += adaptor_priv->pitch; + } + + drm_intel_gem_bo_unmap_gtt(new_frame); + + /* update cliplist */ + if (!REGION_EQUAL(scrn->pScreen, &adaptor_priv->clip, clipBoxes)) { + REGION_COPY(scrn->pScreen, &adaptor_priv->clip, clipBoxes); + xf86XVFillKeyHelperDrawable(drawable, adaptor_priv->colorKey, + clipBoxes); + } + + if (plane_updated) { + ret = drmModeSetPlane(intel->drmSubFD, plane_id, + crtc_id, adaptor_priv->fb_id, + crtc_x, crtc_y, crtc_w, crtc_h, + 0, 0, pixel_width, lines); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to enable plane for video data: %s\n", + strerror(errno)); + return BadAlloc; + } + } + + return Success; +} + +static int intel_sprite_query_attrs(ScrnInfoPtr scrn, int id, unsigned short *w, + unsigned short *h, int *pitches, + int *offsets) +{ + int size; + + if (*w > IMAGE_MAX_WIDTH) + *w = IMAGE_MAX_WIDTH; + if (*h > IMAGE_MAX_HEIGHT) + *h = IMAGE_MAX_HEIGHT; + + *w = (*w + 1) & ~1; + if (offsets) + offsets[0] = 0; + + switch (id) { + case FOURCC_YUY2: + default: + size = *w << 1; + if (pitches) + pitches[0] = size; + size *= *h; + break; + } + + return size; +} + +XF86VideoAdaptorPtr intel_setup_sprite(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(scrn); + XF86VideoAdaptorPtr adapt; + intel_adaptor_private *adaptor_priv; + drmModePlaneRes *plane_resources; + drmModePlane *planes; + + adapt = calloc(1, sizeof(XF86VideoAdaptorRec) + + sizeof(intel_adaptor_private) + sizeof(DevUnion)); + if (!adapt) + goto err; + + plane_resources = drmModeGetPlaneResources(intel->drmSubFD); + if (!plane_resources) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, "No sprite support\n"); + goto err_free_adaptor; + } + + planes = calloc(plane_resources->count_planes, sizeof(*planes)); + if (!planes) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Out of memory in %s\n", + __func__); + goto err_free_res; + } + + adapt->type = XvWindowMask | XvInputMask | XvImageMask; + adapt->flags = VIDEO_OVERLAID_IMAGES /*| VIDEO_CLIP_TO_VIEWPORT */ ; + adapt->name = "Intel(R) Video Sprite"; + adapt->nEncodings = ARRAY_SIZE(xv_dummy_encoding); + adapt->pEncodings = xnfalloc(sizeof(xv_dummy_encoding)); + memcpy(adapt->pEncodings, xv_dummy_encoding, sizeof(xv_dummy_encoding)); + adapt->nFormats = ARRAY_SIZE(xv_formats); + adapt->pFormats = xv_formats; + adapt->nPorts = 1; + adapt->pPortPrivates = (DevUnion *) (&adapt[1]); + + adaptor_priv = (intel_adaptor_private *)&adapt->pPortPrivates[1]; + adapt->pPortPrivates[0].ptr = (pointer) (adaptor_priv); + + adapt->nAttributes = ARRAY_SIZE(attribs); + adapt->pAttributes = attribs; + + adapt->nImages = ARRAY_SIZE(xv_images); + adapt->pImages = xv_images; + + adapt->PutVideo = NULL; + adapt->PutStill = NULL; + adapt->GetVideo = NULL; + adapt->GetStill = NULL; + adapt->StopVideo = intel_sprite_stop; + adapt->SetPortAttribute = intel_sprite_set_attr; + adapt->GetPortAttribute = intel_sprite_get_attr; + adapt->QueryBestSize = intel_sprite_best_size; + adapt->PutImage = intel_sprite_put; + adapt->QueryImageAttributes = intel_sprite_query_attrs; + + adaptor_priv->textured = FALSE; + adaptor_priv->colorKey = intel->colorKey & ((1 << scrn->depth) - 1); + adaptor_priv->brightness = -19; /* (255/219) * -16 */ + adaptor_priv->contrast = 75; /* 255/219 * 64 */ + adaptor_priv->saturation = 146; /* 128/112 * 128 */ + adaptor_priv->desired_crtc = NULL; + adaptor_priv->buf = NULL; + adaptor_priv->old_buf[0] = NULL; + adaptor_priv->old_buf[1] = NULL; + adaptor_priv->gamma5 = 0xc0c0c0; + adaptor_priv->gamma4 = 0x808080; + adaptor_priv->gamma3 = 0x404040; + adaptor_priv->gamma2 = 0x202020; + adaptor_priv->gamma1 = 0x101010; + adaptor_priv->gamma0 = 0x080808; + adaptor_priv->planes = planes; + + adaptor_priv->rotation = RR_Rotate_0; + + /* gotta uninit this someplace */ + REGION_NULL(screen, &adaptor_priv->clip); + + intel->sprite_adaptor = adapt; + + xvColorKey = MAKE_ATOM("XV_COLORKEY"); + + free(plane_resources); + + return adapt; + +err_free_res: + free(plane_resources); +err_free_adaptor: + free(adapt); +err: + return NULL; +} diff --git a/src/intel_video.c b/src/intel_video.c index 021ca5fe..c873d34e 100644 --- a/src/intel_video.c +++ b/src/intel_video.c @@ -337,7 +337,8 @@ void I830InitVideo(ScreenPtr screen) ScrnInfoPtr scrn = xf86Screens[screen->myNum]; intel_screen_private *intel = intel_get_screen_private(scrn); XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL; - XF86VideoAdaptorPtr overlayAdaptor = NULL, texturedAdaptor = NULL; + XF86VideoAdaptorPtr overlayAdaptor = NULL, texturedAdaptor = NULL, + spriteAdaptor = NULL; int num_adaptors; num_adaptors = xf86XVListGenericAdaptors(scrn, &adaptors); @@ -345,7 +346,7 @@ void I830InitVideo(ScreenPtr screen) * adaptors. */ newAdaptors = - malloc((num_adaptors + 2) * sizeof(XF86VideoAdaptorPtr *)); + malloc((num_adaptors + 3) * sizeof(XF86VideoAdaptorPtr *)); if (newAdaptors == NULL) return; @@ -388,6 +389,13 @@ void I830InitVideo(ScreenPtr screen) } } + + spriteAdaptor = intel_setup_sprite(screen); + if (spriteAdaptor) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, "Set up video sprite\n"); + adaptors[num_adaptors++] = spriteAdaptor; + } + if (overlayAdaptor && intel->XvPreferOverlay) adaptors[num_adaptors++] = overlayAdaptor; @@ -405,10 +413,9 @@ void I830InitVideo(ScreenPtr screen) intel->XvEnabled = FALSE; } -#ifdef INTEL_XVMC if (texturedAdaptor) intel_xvmc_adaptor_init(screen); -#endif + free(adaptors); } @@ -1226,7 +1233,7 @@ intel_display_overlay(ScrnInfoPtr scrn, xf86CrtcPtr crtc, src_w, src_h, drw_w, drw_h); } -static Bool +Bool intel_clip_video_helper(ScrnInfoPtr scrn, intel_adaptor_private *adaptor_priv, xf86CrtcPtr * crtc_ret, diff --git a/src/intel_video.h b/src/intel_video.h index f405d40b..22d9bc93 100644 --- a/src/intel_video.h +++ b/src/intel_video.h @@ -39,6 +39,7 @@ typedef struct { RegionRec clip; uint32_t colorKey; + Bool colorKeyChanged; uint32_t gamma0; uint32_t gamma1; @@ -55,6 +56,18 @@ typedef struct { drm_intel_bo *buf, *old_buf[2]; Bool reusable; + /* For kernel level plane updates */ + unsigned long pitch; + int fb_id; + uint32_t crtc_id; + int plane_id; + uint32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + uint32_t src_x, src_y; + uint32_t src_w, src_h; + + struct _drmModePlane *planes; + Bool textured; Rotation rotation; /* should remove intel->rotation later */ @@ -67,6 +80,12 @@ intel_get_adaptor_private(intel_screen_private *intel) return intel->adaptor->pPortPrivates[0].ptr; } +static inline intel_adaptor_private * +intel_get_sprite_adaptor_private(intel_screen_private *intel) +{ + return intel->sprite_adaptor->pPortPrivates[0].ptr; +} + void I915DisplayVideoTextured(ScrnInfoPtr scrn, intel_adaptor_private *adaptor_priv, int id, RegionPtr dstRegion, short width, @@ -93,3 +112,16 @@ void i965_free_video(ScrnInfoPtr scrn); int is_planar_fourcc(int id); void intel_video_block_handler(intel_screen_private *intel); + +extern Bool intel_clip_video_helper(ScrnInfoPtr scrn, + intel_adaptor_private *adaptor_priv, + xf86CrtcPtr * crtc_ret, + BoxPtr dst, + short src_x, short src_y, + short drw_x, short drw_y, + short src_w, short src_h, + short drw_w, short drw_h, + int id, + int *top, int* left, int* npixels, + int *nlines, + RegionPtr reg, INT32 width, INT32 height); -- cgit v1.2.3