chromium/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.c

/* Copyright 2013 The Chromium Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file. */

#include <stdlib.h>
#include <string.h>

#include "ppapi/c/pp_rect.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_size.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppb_graphics_2d.h"
#include "ppapi/c/ppb_image_data.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_view.h"

#include "ppapi_simple/ps.h"
#include "ppapi_simple/ps_context_2d.h"
#include "ppapi_simple/ps_event.h"
#include "ppapi_simple/ps_instance.h"
#include "ppapi_simple/ps_interface.h"

PSContext2D_t* PSContext2DAllocate(PP_ImageDataFormat format) {
  PSContext2D_t* ctx = (PSContext2D_t*)malloc(sizeof(PSContext2D_t));
  memset(ctx, 0, sizeof(PSContext2D_t));

  ctx->format = format;
  return ctx;
}

void PSContext2DFree(PSContext2D_t* ctx) {
  if (ctx->graphic_2d) {
    PSInterfaceCore()->ReleaseResource(ctx->graphic_2d);
    ctx->graphic_2d = 0;
  }
  if (ctx->image) {
    PSInterfaceCore()->ReleaseResource(ctx->image);
    ctx->image = 0;
  }
  free(ctx);
}

PP_ImageDataFormat PSContext2DGetNativeImageDataFormat() {
  return PSInterfaceImageData()->GetNativeImageDataFormat();
}

// Update the 2D context if the message is appropriate, returning non-zero
// if the event was consumed.
int PSContext2DHandleEvent(PSContext2D_t* ctx, PSEvent* event) {
  switch (event->type) {
    case PSE_INSTANCE_DIDCHANGEVIEW: {
      struct PP_Rect rect;

      PSInterfaceView()->GetRect(event->as_resource, &rect);
      PSInterfaceCore()->ReleaseResource(ctx->graphic_2d);
      ctx->bound = 0;
      ctx->width = rect.size.width;
      ctx->height = rect.size.height;

      // Create an opaque graphic context of the specified size.
      ctx->graphic_2d = PSInterfaceGraphics2D()->Create(PSGetInstanceId(),
                                                        &rect.size, PP_TRUE);

      // Bind the context to so that draws will be visible.
      if (ctx->graphic_2d) {
        ctx->bound = PSInterfaceInstance()->BindGraphics(PSGetInstanceId(),
                                                         ctx->graphic_2d);
      }

      // Typically this resource would not be allocated yet, but just in case
      // throw it away, to force a new allocation when GetBuffer is called.
      if (ctx->image) {
        PSInterfaceCore()->ReleaseResource(ctx->image);
        ctx->image = 0;
      }

      return 1;
    }
    default:
      break;
  }

  return 0;
}

// Allocates (if needed) a new image context which will be swapped in when
// drawing is complete.  PSContextGetBuffer and PSContext2DSwapBuffer
// implemented the suggested image/graphic_2d use specified in the
// ppb_graphics_2d header.
int PSContext2DGetBuffer(PSContext2D_t* ctx) {
  if (!ctx->bound)
    return 0;

  // Check if we are already holding an image
  if (ctx->image)
    return 1;

  struct PP_Size size;
  size.width = ctx->width;
  size.height = ctx->height;

  // Allocate a new image resource with the specified size and format, but
  // do not ZERO out the buffer first since we will fill it.
  PP_Resource image = PSInterfaceImageData()->Create(
      PSGetInstanceId(), ctx->format, &size, PP_FALSE);

  if (0 == image) {
    PSInstanceError("Unable to create 2D image.\n");
    return 0;
  }

  // Get the stride
  struct PP_ImageDataDesc desc;
  PSInterfaceImageData()->Describe(image, &desc);

  ctx->image = image;
  ctx->data = (uint32_t*)(PSInterfaceImageData()->Map(image));
  ctx->stride = desc.stride;
  return 1;
}

int PSContext2DSwapBuffer(PSContext2D_t* ctx) {
  if (ctx->bound && ctx->image) {
    PSInterfaceImageData()->Unmap(ctx->image);
    PSInterfaceGraphics2D()->ReplaceContents(ctx->graphic_2d, ctx->image);
    PSInterfaceGraphics2D()->Flush(ctx->graphic_2d, PP_BlockUntilComplete());
    PSInterfaceCore()->ReleaseResource(ctx->image);

    ctx->image = 0;
    ctx->stride = 0;
    ctx->data = NULL;
    return 1;
  }
  return 0;
}