Esempio n. 1
0
int FindRoot(Node* tree, int length) {
  int i = length;
  while(GETN(tree, i).parnode != -1) {
    i = GETN(tree, i).parnode;    
  }
  return i;
}
Esempio n. 2
0
static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
                         char *Contents, uint32_t *Start, uint32_t *Length )
{
  *Flags = GETN2(20);
  *Number = GETN2(22);
  GETN(24, 32, Contents);
  *Start = GETN4(188);
  *Length = GETN4(192);
  return 0;
}
Esempio n. 3
0
static int UDFPartition( quint8 *data, quint16 *Flags, quint16 *Number,
                         char *Contents, quint32 *Start, quint32 *Length )
{
  *Flags = GETN2(20);
  *Number = GETN2(22);
  GETN(24, 32, Contents);
  *Start = GETN4(188);
  *Length = GETN4(192);
  return 0;
}
Esempio n. 4
0
// post order
// This is a minor version of Boxer function used for packing 
void MiniBoxer(Node* tree, int child, int parent) { 
  if(GETN(tree, child).cutline == 'V') {
    GETN(tree, child).width = SUM(RIGHTC(tree, child).width, LEFTC(tree, child).width);
    GETN(tree, child).height = MAX(RIGHTC(tree, child).height, LEFTC(tree, child).height);
  }
  else {
    GETN(tree, child).height = SUM(RIGHTC(tree, child).height, LEFTC(tree, child).height);
    GETN(tree, child).width = MAX(RIGHTC(tree, child).width, LEFTC(tree, child).width);
  }

  if(GETN(tree, parent).cutline == 'V') {
    GETN(tree, parent).width = SUM(RIGHTC(tree, parent).width, LEFTC(tree, parent).width);
    GETN(tree, parent).height = MAX(RIGHTC(tree, parent).height, LEFTC(tree, parent).height);
  }
  else {
    GETN(tree, parent).height = SUM(RIGHTC(tree, parent).height, LEFTC(tree, parent).height);
    GETN(tree, parent).width = MAX(RIGHTC(tree, parent).width, LEFTC(tree, parent).width);
  }
}
Esempio n. 5
0
// Set min height and width of Boxes
void Boxer(Node* tree, int n) {
  if(GETN(tree, n).cutline == '-') {
    return;
  }
  Boxer(tree, GETN(tree, n).left);
  Boxer(tree, GETN(tree, n).right);
  if(GETN(tree, n).cutline == 'V') {
    GETN(tree, n).width = SUM(RIGHTC(tree, n).width, LEFTC(tree, n).width);
    GETN(tree, n).height = MAX(RIGHTC(tree, n).height, LEFTC(tree, n).height);
  }
  else {
    GETN(tree, n).height = SUM(RIGHTC(tree, n).height, LEFTC(tree, n).height);
    GETN(tree, n).width = MAX(RIGHTC(tree, n).width, LEFTC(tree, n).width);
  }
  return;
}
Esempio n. 6
0
int main(int argc, char** argv) {
  if(argc < 3) {
    printf("Not enough arguments.\n");
    return EXIT_FAILURE;
  }
  int numrecs;
  int length;
  Node* tree = LoadFile(argv[1], &length, &numrecs);
  if(tree == NULL) {
    return EXIT_FAILURE;
  }
  clock_t pack_start = clock();
  int root = FindRoot(tree, length);
  Pack(tree, root);
  clock_t pack_end = clock();
  double packtime = (double) (pack_end - pack_start) / CLOCKS_PER_SEC;

  printf("Width:  %le\n", GETN(tree, root).width);
  printf("Height:  %le\n\n", GETN(tree, root).height);
  printf("X-coordinate:  %le\n", GETN(tree, numrecs).xcoord);
  printf("Y-coordinate:  %le\n\n", GETN(tree, numrecs).ycoord);
  printf("Elapsed time:  %le\n", packtime);
  printf("\n");

  Box bests = { 
    GETN(tree, root).width, 
    GETN(tree, root).height
  };

  clock_t reroot_start = clock();
  // void ReRoot(Node* tree, int root, Box* min, int nogo_root)
  ReRoot(tree, root, &bests, GETN(tree, root).right); // deal with root's left child 
  ReRoot(tree, root, &bests, GETN(tree, root).left); // deal with root's right child 
  clock_t reroot_end = clock();
  double reroottime = (double) (reroot_end - reroot_start) / CLOCKS_PER_SEC;

  // Re Rooting
  printf("Best width:  %le\n", bests.width);
  printf("Best height:  %le\n\n", bests.height);
  printf("Elapsed time for re-rooting:  %le\n", reroottime);

  int error_code = SaveFile(argv[2], tree, numrecs);
  return error_code;
}
Esempio n. 7
0
PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
{
  if (!gs)
    return PR_FAILURE;

  /* If we fail, some upstream data provider ignored the
     zero return value from il_gif_write_ready() which says not to
     send any more data to this stream until the delay timeout fires. */
  if ((len != 0) && (gs->gathered >= MAX_READ_AHEAD))
    return PR_FAILURE;

  const PRUint8 *q, *p = buf, *ep = buf + len;

  q = nsnull;                   /* Initialize to shut up gcc warnings */

  while (p <= ep) {
    switch (gs->state)
    {
    case gif_lzw:
      if (do_lzw(gs, q) < 0) {
        gs->state = gif_error;
        break;
      }
      GETN(1, gif_sub_block);
      break;

    case gif_lzw_start:
    {
      /* Initialize LZW parser/decoder */
      gs->datasize = *q;
      if (gs->datasize >= MAX_LZW_BITS) {
        gs->state = gif_error;
        break;
      }

      gs->clear_code = 1 << gs->datasize;
      gs->avail = gs->clear_code + 2;
      gs->oldcode = -1;
      gs->codesize = gs->datasize + 1;
      gs->codemask = (1 << gs->codesize) - 1;

      gs->datum = gs->bits = 0;

      if (gs->clear_code >= MAX_BITS) {
        gs->state = gif_error;
        break;
      }

      /* init the tables */
      for (int i = 0; i < gs->clear_code; i++)
        gs->suffix[i] = i;

      gs->stackp = gs->stack;

      GETN(1, gif_sub_block);
    }
    break;

    /* We're positioned at the very start of the file. */
    case gif_init:
    {
      GETN(3, gif_type);
      break;
    }

    /* All GIF files begin with "GIF87a" or "GIF89a" */
    case gif_type:
    {
      if (strncmp((char*)q, "GIF", 3)) {
        gs->state = gif_error;
        break;
      }
      GETN(3, gif_version);
    }
    break;

    case gif_version:
    {
      if (!strncmp((char*)q, "89a", 3)) {
        gs->version = 89;
      } else if (!strncmp((char*)q, "87a", 3)) {
        gs->version = 87;
      } else {
        gs->state = gif_error;
        break;
      }
      GETN(7, gif_global_header);
    }
    break;

    case gif_global_header:
    {
      /* This is the height and width of the "screen" or
       * frame into which images are rendered.  The
       * individual images can be smaller than the
       * screen size and located with an origin anywhere
       * within the screen.
       */

      gs->screen_width = GETINT16(q);
      gs->screen_height = GETINT16(q + 2);

      gs->screen_bgcolor = q[5];

      gs->global_colormap_size = 2<<(q[4]&0x07);

      // XXX make callback
      nsGIFDecoder2::BeginGIF(
        gs->clientptr,
        gs->screen_width,
        gs->screen_height,
        gs->screen_bgcolor);

      if (q[4] & 0x80) /* global map */
        /* 3 bytes for each entry in the global colormap */
        GETN(gs->global_colormap_size*3, gif_global_colormap);
      else
        GETN(1, gif_image_start);

      // q[6] = Pixel Aspect Ratio
      //   Not used
      //   float aspect = (float)((q[6] + 15) / 64.0);
    }
    break;

    case gif_global_colormap:
    {
      memcpy(gs->global_colormap, q, 3 * gs->global_colormap_size);

      GETN(1, gif_image_start);
    }
    break;

    case gif_image_start:
    {
      if (*q == ';') { /* terminator */
        gs->state = gif_done;
        break;
      }

      if (*q == '!') { /* extension */
        GETN(2, gif_extension);
        break;
      }

      /* If we get anything other than ',' (image separator), '!'
       * (extension), or ';' (trailer), there is extraneous data
       * between blocks. The GIF87a spec tells us to keep reading
       * until we find an image separator, but GIF89a says such
       * a file is corrupt. We follow GIF89a and bail out. */
      if (*q != ',') {
        if (gs->images_decoded > 0) {
          /* The file is corrupt, but one or more images have
           * been decoded correctly. In this case, we proceed
           * as if the file were correctly terminated and set
           * the state to gif_done, so the GIF will display.
           */
          gs->state = gif_done;
        } else {
          /* No images decoded, there is nothing to display. */
          gs->state = gif_error;
        }
        break;
      } else
        GETN(9, gif_image_header);
    }
    break;

    case gif_extension:
    {
      int len = gs->count = q[1];
      gstate es = gif_skip_block;

      switch (*q)
      {
      case 0xf9:
        es = gif_control_extension;
        break;

      case 0x01:
        // ignoring plain text extension
        break;

      case 0xff:
        es = gif_application_extension;
        break;

      case 0xfe:
        es = gif_consume_comment;
        break;
      }

      if (len)
        GETN(len, es);
      else
        GETN(1, gif_image_start);
    }
    break;

    case gif_consume_block:
      if (!*q)
        GETN(1, gif_image_start);
      else
        GETN(*q, gif_skip_block);
    break;

    case gif_skip_block:
      GETN(1, gif_consume_block);
      break;

    case gif_control_extension:
    {
      if (*q & 0x1) {
        gs->tpixel = q[3];
        gs->is_transparent = PR_TRUE;
      } else {
        gs->is_transparent = PR_FALSE;
        // ignoring gfx control extension
      }
      gs->disposal_method = (gdispose)(((*q) >> 2) & 0x7);
      // Some specs say 3rd bit (value 4), other specs say value 3
      // Let's choose 3 (the more popular)
      if (gs->disposal_method == 4)
        gs->disposal_method = (gdispose)3;
      gs->delay_time = GETINT16(q + 1) * 10;
      GETN(1, gif_consume_block);
    }
    break;

    case gif_comment_extension:
    {
      gs->count = *q;
      if (gs->count)
        GETN(gs->count, gif_consume_comment);
      else
        GETN(1, gif_image_start);
    }
    break;

    case gif_consume_comment:
      GETN(1, gif_comment_extension);
    break;

    case gif_application_extension:
      /* Check for netscape application extension */
      if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
        !strncmp((char*)q, "ANIMEXTS1.0", 11))
        GETN(1, gif_netscape_extension_block);
      else
        GETN(1, gif_consume_block);
    break;

    /* Netscape-specific GIF extension: animation looping */
    case gif_netscape_extension_block:
      if (*q)
        GETN(*q, gif_consume_netscape_extension);
      else
        GETN(1, gif_image_start);
    break;

    /* Parse netscape-specific application extensions */
    case gif_consume_netscape_extension:
    {
      int netscape_extension = q[0] & 7;

      /* Loop entire animation specified # of times.  Only read the
         loop count during the first iteration. */
      if (netscape_extension == 1) {
        gs->loop_count = GETINT16(q + 1);

        /* Zero loop count is infinite animation loop request */
        if (gs->loop_count == 0)
          gs->loop_count = -1;

        GETN(1, gif_netscape_extension_block);
      }
      /* Wait for specified # of bytes to enter buffer */
      else if (netscape_extension == 2) {
        // Don't do this, this extension doesn't exist (isn't used at all) 
        // and doesn't do anything, as our streaming/buffering takes care of it all...
        // See: http://semmix.pl/color/exgraf/eeg24.htm
        GETN(1, gif_netscape_extension_block);
      } else
        gs->state = gif_error; // 0,3-7 are yet to be defined netscape
                               // extension codes

      break;
    }

    case gif_image_header:
    {
      PRUintn height, width;

      /* Get image offsets, with respect to the screen origin */
      gs->x_offset = GETINT16(q);
      gs->y_offset = GETINT16(q + 2);

      /* Get image width and height. */
      width  = GETINT16(q + 4);
      height = GETINT16(q + 6);

      /* Work around broken GIF files where the logical screen
       * size has weird width or height.  We assume that GIF87a
       * files don't contain animations.
       */
      if ((gs->images_decoded == 0) &&
          ((gs->screen_height < height) || (gs->screen_width < width) ||
           (gs->version == 87)))
      {
        gs->screen_height = height;
        gs->screen_width = width;
        gs->x_offset = 0;
        gs->y_offset = 0;

        nsGIFDecoder2::BeginGIF(gs->clientptr,
                                gs->screen_width,
                                gs->screen_height,
                                gs->screen_bgcolor);
      }

      /* Work around more broken GIF files that have zero image
         width or height */
      if (!height || !width) {
        height = gs->screen_height;
        width = gs->screen_width;
        if (!height || !width) {
          gs->state = gif_error;
          break;
        }
      }

      gs->height = height;
      gs->width = width;

      nsGIFDecoder2::BeginImageFrame(gs->clientptr,
                                     gs->images_decoded + 1,   /* Frame number, 1-n */
                                     gs->x_offset,  /* X offset in logical screen */
                                     gs->y_offset,  /* Y offset in logical screen */
                                     width,
                                     height);

      /* This case will never be taken if this is the first image */
      /* being decoded. If any of the later images are larger     */
      /* than the screen size, we need to reallocate buffers.     */
      if (gs->screen_width < width) {
        /* XXX Deviant! */

        gs->rowbuf = (PRUint8*)PR_REALLOC(gs->rowbuf, width);

        if (!gs->rowbuf) {
          gs->state = gif_oom;
          break;
        }

        gs->screen_width = width;
        if (gs->screen_height < gs->height)
          gs->screen_height = gs->height;

      }
      else {
        if (!gs->rowbuf)
          gs->rowbuf = (PRUint8*)PR_MALLOC(gs->screen_width);
      }

      if (!gs->rowbuf) {
          gs->state = gif_oom;
          break;
      }

      if (q[8] & 0x40) {
        gs->interlaced = PR_TRUE;
        gs->ipass = 1;
      } else {
        gs->interlaced = PR_FALSE;
        gs->ipass = 0;
      }

      if (gs->images_decoded == 0) {
        gs->progressive_display = PR_TRUE;
      } else {
        /* Overlaying interlaced, transparent GIFs over
           existing image data using the Haeberli display hack
           requires saving the underlying image in order to
           avoid jaggies at the transparency edges.  We are
           unprepared to deal with that, so don't display such
           images progressively */
        gs->progressive_display = PR_FALSE;
      }

      /* Clear state from last image */
      gs->irow = 0;
      gs->rows_remaining = gs->height;
      gs->rowend = gs->rowbuf + gs->width;
      gs->rowp = gs->rowbuf;

      /* bits per pixel is 1<<((q[8]&0x07) + 1); */

      if (q[8] & 0x80) /* has a local colormap? */
      {
        int num_colors = 2 << (q[8] & 0x7);

        // If current local_colormap is not big enough, force reallocation
        if (num_colors > gs->local_colormap_size)
          PR_FREEIF(gs->local_colormap);
        gs->local_colormap_size = num_colors;

        /* Switch to the new local palette after it loads */
        gs->is_local_colormap_defined = PR_TRUE;
        GETN(gs->local_colormap_size * 3, gif_image_colormap);
      } else {
        /* Switch back to the global palette */
        gs->is_local_colormap_defined = PR_FALSE;
        GETN(1, gif_lzw_start);
      }
    }
    break;

    case gif_image_colormap:
    {
      PRUint8 *map = gs->local_colormap;
      if (!map) {
        map = gs->local_colormap = (PRUint8*)PR_MALLOC(3 * gs->local_colormap_size);
        if (!map) {
          gs->state = gif_oom;
          break;
        }
      }

      memcpy(map, q, 3 * gs->local_colormap_size);

      GETN(1, gif_lzw_start);
    }
    break;

    case gif_sub_block:
    {
      if ((gs->count = *q) != 0)
      /* Still working on the same image: Process next LZW data block */
      {
        /* Make sure there are still rows left. If the GIF data */
        /* is corrupt, we may not get an explicit terminator.   */
        if (gs->rows_remaining == 0) {
          /* This is an illegal GIF, but we remain tolerant. */
#ifdef DONT_TOLERATE_BROKEN_GIFS
          gs->state = gif_error;
          break;
#else
          GETN(1, gif_sub_block);
#endif
        }
        GETN(gs->count, gif_lzw);
      }
      else
      /* See if there are any more images in this sequence. */
      {
        gs->images_decoded++;

        nsGIFDecoder2::EndImageFrame(gs->clientptr,
                                     gs->images_decoded,
                                     gs->delay_time);

        /* Clear state from this image */
        gs->is_transparent = PR_FALSE;

        /* An image can specify a delay time before which to display
           subsequent images. */
        if (gs->delay_time < MINIMUM_DELAY_TIME)
          gs->delay_time = MINIMUM_DELAY_TIME;

        GETN(1, gif_image_start);
      }
    }
    break;

    case gif_done:
      nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
      return PR_SUCCESS;
      break;

    case gif_delay:
    case gif_gather:
    {
      PRInt32 gather_remaining;
      PRInt32 request_size = gs->gather_request_size;

      {
        gather_remaining = request_size - gs->gathered;

        /* Do we already have enough data in the accumulation
           buffer to satisfy the request ?  (This can happen
           after we transition from the gif_delay state.) */
        if (gather_remaining <= 0) {
          gs->gathered -= request_size;
          q = gs->gather_head;
          gs->gather_head += request_size;
          gs->state = gs->post_gather_state;
          break;
        }

        /* Shift remaining data to the head of the buffer */
        if (gs->gathered && (gs->gather_head != gs->hold)) {
          memmove(gs->hold, gs->gather_head, gs->gathered);
          gs->gather_head = gs->hold;
        }

        /* If we add the data just handed to us by the netlib
           to what we've already gathered, is there enough to satisfy
           the current request ? */
        if ((ep - p) >= gather_remaining) {
          if (gs->gathered) { /* finish a prior gather */
            char *hold = (char*)gs->hold;
            BlockAllocCat(hold, gs->gathered, (char*)p, gather_remaining);
            gs->hold = (PRUint8*)hold;
            q = gs->gather_head = gs->hold;
            gs->gathered = 0;
          } else
            q = p;

          p += gather_remaining;
          gs->state = gs->post_gather_state;
        } else {
          char *hold = (char*)gs->hold;
          BlockAllocCat(hold, gs->gathered, (char*)p, ep - p);
          gs->hold = (PRUint8*)hold;
          gs->gather_head = gs->hold;
          gs->gathered += ep-p;
          return PR_SUCCESS;
        }
      }
    }
    break;

    // Handle out of memory errors
    case gif_oom:
      return PR_FAILURE;

    // Handle general errors
    case gif_error:
      nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
      return PR_SUCCESS;

    case gif_stop_animating:
      return PR_SUCCESS;

    // We shouldn't ever get here.
    default:
      break;
    }
  }

  return PR_SUCCESS;
}
Esempio n. 8
0
// Parse incoming GIF data stream into internal data structures.
SkCodec::Result SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
{
    if (m_parseCompleted) {
        return SkCodec::kSuccess;
    }

    if (SkGIFLoopCountQuery == query && m_loopCount != cLoopCountNotSeen) {
        // Loop count has already been parsed.
        return SkCodec::kSuccess;
    }

    // SkGIFSizeQuery and SkGIFFrameCountQuery are negative, so this is only meaningful when >= 0.
    const int lastFrameToParse = (int) query;
    if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse
                && m_frames[lastFrameToParse]->isComplete()) {
        // We have already parsed this frame.
        return SkCodec::kSuccess;
    }

    while (true) {
        if (!m_streamBuffer.buffer(m_bytesToConsume)) {
            // The stream does not yet have enough data.
            return SkCodec::kIncompleteInput;
        }

        switch (m_state) {
        case SkGIFLZW: {
            SkASSERT(!m_frames.empty());
            auto* frame = m_frames.back().get();
            frame->addLzwBlock(m_streamBuffer.markPosition(), m_bytesToConsume);
            GETN(1, SkGIFSubBlock);
            break;
        }
        case SkGIFLZWStart: {
            SkASSERT(!m_frames.empty());
            auto* currentFrame = m_frames.back().get();

            currentFrame->setDataSize(this->getOneByte());
            GETN(1, SkGIFSubBlock);
            break;
        }

        case SkGIFType: {
            const char* currentComponent = m_streamBuffer.get();

            // All GIF files begin with "GIF87a" or "GIF89a".
            if (!memcmp(currentComponent, "GIF89a", 6))
                m_version = 89;
            else if (!memcmp(currentComponent, "GIF87a", 6))
                m_version = 87;
            else {
                // This prevents attempting to continue reading this invalid stream.
                GETN(0, SkGIFDone);
                return SkCodec::kInvalidInput;
            }
            GETN(7, SkGIFGlobalHeader);
            break;
        }

        case SkGIFGlobalHeader: {
            const unsigned char* currentComponent =
                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

            // This is the height and width of the "screen" or frame into which
            // images are rendered. The individual images can be smaller than
            // the screen size and located with an origin anywhere within the
            // screen.
            // Note that we don't inform the client of the size yet, as it might
            // change after we read the first frame's image header.
            fScreenWidth = GETINT16(currentComponent);
            fScreenHeight = GETINT16(currentComponent + 2);

            const int globalColorMapColors = 2 << (currentComponent[4] & 0x07);

            if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* global map */
                m_globalColorMap.setNumColors(globalColorMapColors);
                GETN(SK_BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, SkGIFGlobalColormap);
                break;
            }

            GETN(1, SkGIFImageStart);
            break;
        }

        case SkGIFGlobalColormap: {
            m_globalColorMap.setTablePosition(m_streamBuffer.markPosition());
            GETN(1, SkGIFImageStart);
            break;
        }

        case SkGIFImageStart: {
            const char currentComponent = m_streamBuffer.get()[0];

            if (currentComponent == '!') { // extension.
                GETN(2, SkGIFExtension);
                break;
            }

            if (currentComponent == ',') { // image separator.
                GETN(9, SkGIFImageHeader);
                break;
            }

            // If we get anything other than ',' (image separator), '!'
            // (extension), or ';' (trailer), there is extraneous data
            // between blocks. The GIF87a spec tells us to keep reading
            // until we find an image separator, but GIF89a says such
            // a file is corrupt. We follow Mozilla's implementation and
            // proceed as if the file were correctly terminated, so the
            // GIF will display.
            GETN(0, SkGIFDone);
            break;
        }

        case SkGIFExtension: {
            const unsigned char* currentComponent =
                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

            size_t bytesInBlock = currentComponent[1];
            SkGIFState exceptionState = SkGIFSkipBlock;

            switch (*currentComponent) {
            case 0xf9:
                // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes,
                exceptionState = SkGIFControlExtension;
                // and the parser for this block reads 4 bytes, so we must enforce that the buffer
                // contains at least this many bytes. If the GIF specifies a different length, we
                // allow that, so long as it's larger; the additional data will simply be ignored.
                bytesInBlock = std::max(bytesInBlock, static_cast<size_t>(4));
                break;

            // The GIF spec also specifies the lengths of the following two extensions' headers
            // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely
            // and sanity-check the actual length of the application extension header before reading it,
            // we allow GIFs to deviate from these values in either direction. This is important for
            // real-world compatibility, as GIFs in the wild exist with application extension headers
            // that are both shorter and longer than 11 bytes.
            case 0x01:
                // ignoring plain text extension
                break;

            case 0xff:
                exceptionState = SkGIFApplicationExtension;
                break;

            case 0xfe:
                exceptionState = SkGIFConsumeComment;
                break;
            }

            if (bytesInBlock)
                GETN(bytesInBlock, exceptionState);
            else
                GETN(1, SkGIFImageStart);
            break;
        }

        case SkGIFConsumeBlock: {
            const unsigned char currentComponent = this->getOneByte();
            if (!currentComponent)
                GETN(1, SkGIFImageStart);
            else
                GETN(currentComponent, SkGIFSkipBlock);
            break;
        }

        case SkGIFSkipBlock: {
            GETN(1, SkGIFConsumeBlock);
            break;
        }

        case SkGIFControlExtension: {
            const unsigned char* currentComponent =
                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

            addFrameIfNecessary();
            SkGIFFrameContext* currentFrame = m_frames.back().get();
            if (*currentComponent & 0x1)
                currentFrame->setTransparentPixel(currentComponent[3]);

            // We ignore the "user input" bit.

            // NOTE: This relies on the values in the FrameDisposalMethod enum
            // matching those in the GIF spec!
            int rawDisposalMethod = ((*currentComponent) >> 2) & 0x7;
            switch (rawDisposalMethod) {
            case 1:
            case 2:
            case 3:
                currentFrame->setDisposalMethod((SkCodecAnimation::DisposalMethod) rawDisposalMethod);
                break;
            case 4:
                // Some specs say that disposal method 3 is "overwrite previous", others that setting
                // the third bit of the field (i.e. method 4) is. We map both to the same value.
                currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kRestorePrevious);
                break;
            default:
                // Other values use the default.
                currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep);
                break;
            }
            currentFrame->setDuration(GETINT16(currentComponent + 1) * 10);
            GETN(1, SkGIFConsumeBlock);
            break;
        }

        case SkGIFCommentExtension: {
            const unsigned char currentComponent = this->getOneByte();
            if (currentComponent)
                GETN(currentComponent, SkGIFConsumeComment);
            else
                GETN(1, SkGIFImageStart);
            break;
        }

        case SkGIFConsumeComment: {
            GETN(1, SkGIFCommentExtension);
            break;
        }

        case SkGIFApplicationExtension: {
            // Check for netscape application extension.
            if (m_bytesToConsume == 11) {
                const unsigned char* currentComponent =
                    reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

                if (!memcmp(currentComponent, "NETSCAPE2.0", 11) || !memcmp(currentComponent, "ANIMEXTS1.0", 11))
                    GETN(1, SkGIFNetscapeExtensionBlock);
            }

            if (m_state != SkGIFNetscapeExtensionBlock)
                GETN(1, SkGIFConsumeBlock);
            break;
        }

        // Netscape-specific GIF extension: animation looping.
        case SkGIFNetscapeExtensionBlock: {
            const int currentComponent = this->getOneByte();
            // SkGIFConsumeNetscapeExtension always reads 3 bytes from the stream; we should at least wait for this amount.
            if (currentComponent)
                GETN(std::max(3, currentComponent), SkGIFConsumeNetscapeExtension);
            else
                GETN(1, SkGIFImageStart);
            break;
        }

        // Parse netscape-specific application extensions
        case SkGIFConsumeNetscapeExtension: {
            const unsigned char* currentComponent =
                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

            int netscapeExtension = currentComponent[0] & 7;

            // Loop entire animation specified # of times. Only read the loop count during the first iteration.
            if (netscapeExtension == 1) {
                m_loopCount = GETINT16(currentComponent + 1);

                // Zero loop count is infinite animation loop request.
                if (!m_loopCount)
                    m_loopCount = SkCodec::kRepetitionCountInfinite;

                GETN(1, SkGIFNetscapeExtensionBlock);

                if (SkGIFLoopCountQuery == query) {
                    m_streamBuffer.flush();
                    return SkCodec::kSuccess;
                }
            } else if (netscapeExtension == 2) {
                // Wait for specified # of bytes to enter buffer.

                // Don't do this, this extension doesn't exist (isn't used at all)
                // and doesn't do anything, as our streaming/buffering takes care of it all...
                // See: http://semmix.pl/color/exgraf/eeg24.htm
                GETN(1, SkGIFNetscapeExtensionBlock);
            } else {
                // 0,3-7 are yet to be defined netscape extension codes
                // This prevents attempting to continue reading this invalid stream.
                GETN(0, SkGIFDone);
                return SkCodec::kInvalidInput;
            }
            break;
        }

        case SkGIFImageHeader: {
            int height, width, xOffset, yOffset;
            const unsigned char* currentComponent =
                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());

            /* Get image offsets, with respect to the screen origin */
            xOffset = GETINT16(currentComponent);
            yOffset = GETINT16(currentComponent + 2);

            /* Get image width and height. */
            width  = GETINT16(currentComponent + 4);
            height = GETINT16(currentComponent + 6);

            // Some GIF files have frames that don't fit in the specified
            // overall image size. For the first frame, we can simply enlarge
            // the image size to allow the frame to be visible.  We can't do
            // this on subsequent frames because the rest of the decoding
            // infrastructure assumes the image size won't change as we
            // continue decoding, so any subsequent frames that are even
            // larger will be cropped.
            // Luckily, handling just the first frame is sufficient to deal
            // with most cases, e.g. ones where the image size is erroneously
            // set to zero, since usually the first frame completely fills
            // the image.
            if (currentFrameIsFirstFrame()) {
                fScreenHeight = std::max(fScreenHeight, yOffset + height);
                fScreenWidth = std::max(fScreenWidth, xOffset + width);
            }

            // NOTE: Chromium placed this block after setHeaderDefined, down
            // below we returned true when asked for the size. So Chromium
            // created an image which would fail. Is this the correct behavior?
            // We choose to return false early, so we will not create an
            // SkCodec.

            // Work around more broken GIF files that have zero image width or
            // height.
            if (!height || !width) {
                height = fScreenHeight;
                width = fScreenWidth;
                if (!height || !width) {
                    // This prevents attempting to continue reading this invalid stream.
                    GETN(0, SkGIFDone);
                    return SkCodec::kInvalidInput;
                }
            }

            const bool isLocalColormapDefined = SkToBool(currentComponent[8] & 0x80);
            // The three low-order bits of currentComponent[8] specify the bits per pixel.
            const int numColors = 2 << (currentComponent[8] & 0x7);
            if (currentFrameIsFirstFrame()) {
                const int transPix = m_frames.empty() ? SkGIFColorMap::kNotFound
                                                      : m_frames[0]->transparentPixel();
                if (this->hasTransparency(transPix,
                        isLocalColormapDefined, numColors))
                {
                    m_firstFrameHasAlpha = true;
                } else {
                    const bool frameIsSubset = xOffset > 0 || yOffset > 0
                            || width < fScreenWidth
                            || height < fScreenHeight;
                    m_firstFrameHasAlpha = frameIsSubset;
                }
            }

            addFrameIfNecessary();
            SkGIFFrameContext* currentFrame = m_frames.back().get();
            currentFrame->setHeaderDefined();

            if (query == SkGIFSizeQuery) {
                // The decoder needs to stop, so we return here, before
                // flushing the buffer. Next time through, we'll be in the same
                // state, requiring the same amount in the buffer.
                return SkCodec::kSuccess;
            }


            currentFrame->setXYWH(xOffset, yOffset, width, height);
            currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40));

            // Overlaying interlaced, transparent GIFs over
            // existing image data using the Haeberli display hack
            // requires saving the underlying image in order to
            // avoid jaggies at the transparency edges. We are
            // unprepared to deal with that, so don't display such
            // images progressively. Which means only the first
            // frame can be progressively displayed.
            // FIXME: It is possible that a non-transparent frame
            // can be interlaced and progressively displayed.
            currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame());

            if (isLocalColormapDefined) {
                currentFrame->localColorMap().setNumColors(numColors);
                GETN(SK_BYTES_PER_COLORMAP_ENTRY * numColors, SkGIFImageColormap);
                break;
            }

            setAlphaAndRequiredFrame(currentFrame);
            GETN(1, SkGIFLZWStart);
            break;
        }

        case SkGIFImageColormap: {
            SkASSERT(!m_frames.empty());
            auto* currentFrame = m_frames.back().get();
            auto& cmap = currentFrame->localColorMap();
            cmap.setTablePosition(m_streamBuffer.markPosition());
            setAlphaAndRequiredFrame(currentFrame);
            GETN(1, SkGIFLZWStart);
            break;
        }

        case SkGIFSubBlock: {
            const size_t bytesInBlock = this->getOneByte();
            if (bytesInBlock)
                GETN(bytesInBlock, SkGIFLZW);
            else {
                // Finished parsing one frame; Process next frame.
                SkASSERT(!m_frames.empty());
                // Note that some broken GIF files do not have enough LZW blocks to fully
                // decode all rows but we treat it as frame complete.
                m_frames.back()->setComplete();
                GETN(1, SkGIFImageStart);
                if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse) {
                    m_streamBuffer.flush();
                    return SkCodec::kSuccess;
                }
            }
            break;
        }

        case SkGIFDone: {
            m_parseCompleted = true;
            return SkCodec::kSuccess;
        }

        default:
            // We shouldn't ever get here.
            // This prevents attempting to continue reading this invalid stream.
            GETN(0, SkGIFDone);
            return SkCodec::kInvalidInput;
            break;
        }   // switch
        m_streamBuffer.flush();
    }
}
Esempio n. 9
0
void
nsGIFDecoder2::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy)
{
  NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");

  // These variables changed names, and renaming would make a much bigger patch :(
  const uint8_t *buf = (const uint8_t *)aBuffer;
  uint32_t len = aCount;

  const uint8_t *q = buf;

  // Add what we have sofar to the block
  // If previous call to me left something in the hold first complete current block
  // Or if we are filling the colormaps, first complete the colormap
  uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap :
               (mGIFStruct.state == gif_image_colormap) ? (uint8_t*)mColormap :
               (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nullptr;

  if (len == 0 && buf == nullptr) {
    // We've just gotten the frame we asked for. Time to use the data we
    // stashed away.
    len = mGIFStruct.bytes_in_hold;
    q = buf = p;
  } else if (p) {
    // Add what we have sofar to the block
    uint32_t l = std::min(len, mGIFStruct.bytes_to_consume);
    memcpy(p+mGIFStruct.bytes_in_hold, buf, l);

    if (l < mGIFStruct.bytes_to_consume) {
      // Not enough in 'buf' to complete current block, get more
      mGIFStruct.bytes_in_hold += l;
      mGIFStruct.bytes_to_consume -= l;
      return;
    }
    // Point 'q' to complete block in hold (or in colormap)
    q = p;
  }

  // Invariant:
  //    'q' is start of current to be processed block (hold, colormap or buf)
  //    'bytes_to_consume' is number of bytes to consume from 'buf'
  //    'buf' points to the bytes to be consumed from the input buffer
  //    'len' is number of bytes left in input buffer from position 'buf'.
  //    At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
  //    to point to next buffer, 'len' is adjusted accordingly.
  //    So that next round in for loop, q gets pointed to the next buffer.

  for (;len >= mGIFStruct.bytes_to_consume; q=buf, mGIFStruct.bytes_in_hold = 0) {
    // Eat the current block from the buffer, q keeps pointed at current block
    buf += mGIFStruct.bytes_to_consume;
    len -= mGIFStruct.bytes_to_consume;

    switch (mGIFStruct.state)
    {
    case gif_lzw:
      if (!DoLzw(q)) {
        mGIFStruct.state = gif_error;
        break;
      }
      GETN(1, gif_sub_block);
      break;

    case gif_lzw_start:
    {
      // Make sure the transparent pixel is transparent in the colormap
      if (mGIFStruct.is_transparent) {
        // Save old value so we can restore it later
        if (mColormap == mGIFStruct.global_colormap)
            mOldColor = mColormap[mGIFStruct.tpixel];
        mColormap[mGIFStruct.tpixel] = 0;
      }

      /* Initialize LZW parser/decoder */
      mGIFStruct.datasize = *q;
      const int clear_code = ClearCode();
      if (mGIFStruct.datasize > MAX_LZW_BITS ||
          clear_code >= MAX_BITS) {
        mGIFStruct.state = gif_error;
        break;
      }

      mGIFStruct.avail = clear_code + 2;
      mGIFStruct.oldcode = -1;
      mGIFStruct.codesize = mGIFStruct.datasize + 1;
      mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
      mGIFStruct.datum = mGIFStruct.bits = 0;

      /* init the tables */
      for (int i = 0; i < clear_code; i++)
        mGIFStruct.suffix[i] = i;

      mGIFStruct.stackp = mGIFStruct.stack;

      GETN(1, gif_sub_block);
    }
    break;

    /* All GIF files begin with "GIF87a" or "GIF89a" */
    case gif_type:
      if (!strncmp((char*)q, "GIF89a", 6)) {
        mGIFStruct.version = 89;
      } else if (!strncmp((char*)q, "GIF87a", 6)) {
        mGIFStruct.version = 87;
      } else {
        mGIFStruct.state = gif_error;
        break;
      }
      GETN(7, gif_global_header);
      break;

    case gif_global_header:
      /* This is the height and width of the "screen" or
       * frame into which images are rendered.  The
       * individual images can be smaller than the
       * screen size and located with an origin anywhere
       * within the screen.
       */

      mGIFStruct.screen_width = GETINT16(q);
      mGIFStruct.screen_height = GETINT16(q + 2);
      mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;

      if (IsSizeDecode()) {
        MOZ_ASSERT(!mGIFOpen, "Gif should not be open at this point");
        PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
        return;
      }

      // screen_bgcolor is not used
      //mGIFStruct.screen_bgcolor = q[5];
      // q[6] = Pixel Aspect Ratio
      //   Not used
      //   float aspect = (float)((q[6] + 15) / 64.0);

      if (q[4] & 0x80) { /* global map */
        // Get the global colormap
        const uint32_t size = (3 << mGIFStruct.global_colormap_depth);
        if (len < size) {
          // Use 'hold' pattern to get the global colormap
          GETN(size, gif_global_colormap);
          break;
        }
        // Copy everything, go to colormap state to do CMS correction
        memcpy(mGIFStruct.global_colormap, buf, size);
        buf += size;
        len -= size;
        GETN(0, gif_global_colormap);
        break;
      }

      GETN(1, gif_image_start);
      break;

    case gif_global_colormap:
      // Everything is already copied into global_colormap
      // Convert into Cairo colors including CMS transformation
      ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth);
      GETN(1, gif_image_start);
      break;

    case gif_image_start:
      switch (*q) {
        case GIF_TRAILER:
          mGIFStruct.state = gif_done;
          break;

        case GIF_EXTENSION_INTRODUCER:
          GETN(2, gif_extension);
          break;

        case GIF_IMAGE_SEPARATOR:
          GETN(9, gif_image_header);
          break;

        default:
          /* If we get anything other than GIF_IMAGE_SEPARATOR, 
           * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
           * between blocks. The GIF87a spec tells us to keep reading
           * until we find an image separator, but GIF89a says such
           * a file is corrupt. We follow GIF89a and bail out. */
          if (mGIFStruct.images_decoded > 0) {
            /* The file is corrupt, but one or more images have
             * been decoded correctly. In this case, we proceed
             * as if the file were correctly terminated and set
             * the state to gif_done, so the GIF will display.
             */
            mGIFStruct.state = gif_done;
          } else {
            /* No images decoded, there is nothing to display. */
            mGIFStruct.state = gif_error;
          }
      }
      break;

    case gif_extension:
      mGIFStruct.bytes_to_consume = q[1];
      if (mGIFStruct.bytes_to_consume) {
        switch (*q) {
        case GIF_GRAPHIC_CONTROL_LABEL:
          // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes,
          // and the parser for this block reads 4 bytes, so we must enforce that the buffer
          // contains at least this many bytes. If the GIF specifies a different length, we
          // allow that, so long as it's larger; the additional data will simply be ignored.
          mGIFStruct.state = gif_control_extension;
          mGIFStruct.bytes_to_consume = std::max(mGIFStruct.bytes_to_consume, 4u);
          break;

        // The GIF spec also specifies the lengths of the following two extensions' headers
        // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely
        // and sanity-check the actual length of the application extension header before reading it,
        // we allow GIFs to deviate from these values in either direction. This is important for
        // real-world compatibility, as GIFs in the wild exist with application extension headers
        // that are both shorter and longer than 11 bytes.
        case GIF_APPLICATION_EXTENSION_LABEL:
          mGIFStruct.state = gif_application_extension;
          break;

        case GIF_PLAIN_TEXT_LABEL:
          mGIFStruct.state = gif_skip_block;
          break;

        case GIF_COMMENT_LABEL:
          mGIFStruct.state = gif_consume_comment;
          break;

        default:
          mGIFStruct.state = gif_skip_block;
        }
      } else {
        GETN(1, gif_image_start);
      }
      break;

    case gif_consume_block:
      if (!*q)
        GETN(1, gif_image_start);
      else
        GETN(*q, gif_skip_block);
      break;

    case gif_skip_block:
      GETN(1, gif_consume_block);
      break;

    case gif_control_extension:
      mGIFStruct.is_transparent = *q & 0x1;
      mGIFStruct.tpixel = q[3];
      mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
      // Some specs say 3rd bit (value 4), other specs say value 3
      // Let's choose 3 (the more popular)
      if (mGIFStruct.disposal_method == 4)
        mGIFStruct.disposal_method = 3;
      mGIFStruct.delay_time = GETINT16(q + 1) * 10;
      GETN(1, gif_consume_block);
      break;

    case gif_comment_extension:
      if (*q)
        GETN(*q, gif_consume_comment);
      else
        GETN(1, gif_image_start);
      break;

    case gif_consume_comment:
      GETN(1, gif_comment_extension);
      break;

    case gif_application_extension:
      /* Check for netscape application extension */
      if (mGIFStruct.bytes_to_consume == 11 &&
          (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
           !strncmp((char*)q, "ANIMEXTS1.0", 11)))
        GETN(1, gif_netscape_extension_block);
      else
        GETN(1, gif_consume_block);
      break;

    /* Netscape-specific GIF extension: animation looping */
    case gif_netscape_extension_block:
      if (*q)
        // We might need to consume 3 bytes in
        // gif_consume_netscape_extension, so make sure we have at least that.
        GETN(std::max(3, static_cast<int>(*q)), gif_consume_netscape_extension);
      else
        GETN(1, gif_image_start);
      break;

    /* Parse netscape-specific application extensions */
    case gif_consume_netscape_extension:
      switch (q[0] & 7) {
        case 1:
          /* Loop entire animation specified # of times.  Only read the
             loop count during the first iteration. */
          mGIFStruct.loop_count = GETINT16(q + 1);
          GETN(1, gif_netscape_extension_block);
          break;

        case 2:
          /* Wait for specified # of bytes to enter buffer */
          // Don't do this, this extension doesn't exist (isn't used at all)
          // and doesn't do anything, as our streaming/buffering takes care of it all...
          // See: http://semmix.pl/color/exgraf/eeg24.htm
          GETN(1, gif_netscape_extension_block);
          break;

        default:
          // 0,3-7 are yet to be defined netscape extension codes
          mGIFStruct.state = gif_error;
      }
      break;

    case gif_image_header:
    {
      /* Get image offsets, with respect to the screen origin */
      mGIFStruct.x_offset = GETINT16(q);
      mGIFStruct.y_offset = GETINT16(q + 2);

      /* Get image width and height. */
      mGIFStruct.width  = GETINT16(q + 4);
      mGIFStruct.height = GETINT16(q + 6);

      if (!mGIFStruct.images_decoded) {
        /* Work around broken GIF files where the logical screen
         * size has weird width or height.  We assume that GIF87a
         * files don't contain animations.
         */
        if ((mGIFStruct.screen_height < mGIFStruct.height) ||
            (mGIFStruct.screen_width < mGIFStruct.width) ||
            (mGIFStruct.version == 87)) {
          mGIFStruct.screen_height = mGIFStruct.height;
          mGIFStruct.screen_width = mGIFStruct.width;
          mGIFStruct.x_offset = 0;
          mGIFStruct.y_offset = 0;
        }    
        // Create the image container with the right size.
        BeginGIF();
        if (HasError()) {
          // Setting the size led to an error.
          mGIFStruct.state = gif_error;
          return;
        }

        // If we were doing a size decode, we're done
        if (IsSizeDecode())
          return;
      }

      /* Work around more broken GIF files that have zero image
         width or height */
      if (!mGIFStruct.height || !mGIFStruct.width) {
        mGIFStruct.height = mGIFStruct.screen_height;
        mGIFStruct.width = mGIFStruct.screen_width;
        if (!mGIFStruct.height || !mGIFStruct.width) {
          mGIFStruct.state = gif_error;
          break;
        }
      }

      /* Depth of colors is determined by colormap */
      /* (q[8] & 0x80) indicates local colormap */
      /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
      uint32_t depth = mGIFStruct.global_colormap_depth;
      if (q[8] & 0x80)
        depth = (q[8]&0x07) + 1;
      uint32_t realDepth = depth;
      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
        realDepth++;
      } 
      // Mask to limit the color values within the colormap
      mColorMask = 0xFF >> (8 - realDepth);
      BeginImageFrame(realDepth);

      if (NeedsNewFrame()) {
        // We now need a new frame from the decoder framework. We leave all our
        // data in the buffer as if it wasn't consumed, copy to our hold and return
        // to the decoder framework.
        uint32_t size = len + mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold;
        if (size) {
          if (SetHold(q, mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold, buf, len)) {
            // Back into the decoder infrastructure so we can get called again.
            GETN(9, gif_image_header_continue);
            return;
          }
        }
        break;
      } else {
        // FALL THROUGH
      }
    }

    case gif_image_header_continue:
    {
      // While decoders can reuse frames, we unconditionally increment
      // mGIFStruct.images_decoded when we're done with a frame, so we both can
      // and need to zero out the colormap and image data after every new frame.
      memset(mImageData, 0, mImageDataLength);
      if (mColormap) {
        memset(mColormap, 0, mColormapSize);
      }

      if (!mGIFStruct.images_decoded) {
        // Send a onetime invalidation for the first frame if it has a y-axis offset. 
        // Otherwise, the area may never be refreshed and the placeholder will remain
        // on the screen. (Bug 37589)
        if (mGIFStruct.y_offset > 0) {
          nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
          PostInvalidation(r);
        }
      }

      if (q[8] & 0x40) {
        mGIFStruct.interlaced = true;
        mGIFStruct.ipass = 1;
      } else {
        mGIFStruct.interlaced = false;
        mGIFStruct.ipass = 0;
      }

      /* Only apply the Haeberli display hack on the first frame */
      mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);

      /* Clear state from last image */
      mGIFStruct.irow = 0;
      mGIFStruct.rows_remaining = mGIFStruct.height;
      mGIFStruct.rowp = mImageData;

      /* Depth of colors is determined by colormap */
      /* (q[8] & 0x80) indicates local colormap */
      /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
      uint32_t depth = mGIFStruct.global_colormap_depth;
      if (q[8] & 0x80)
        depth = (q[8]&0x07) + 1;
      uint32_t realDepth = depth;
      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
        realDepth++;
      }

      if (q[8] & 0x80) /* has a local colormap? */
      {
        mGIFStruct.local_colormap_size = 1 << depth;
        if (!mGIFStruct.images_decoded) {
          // First frame has local colormap, allocate space for it
          // as the image frame doesn't have its own palette
          mColormapSize = sizeof(uint32_t) << realDepth;
          if (!mGIFStruct.local_colormap) {
            mGIFStruct.local_colormap = (uint32_t*)moz_xmalloc(mColormapSize);
          }
          mColormap = mGIFStruct.local_colormap;
        }
        const uint32_t size = 3 << depth;
        if (mColormapSize > size) {
          // Clear the notfilled part of the colormap
          memset(((uint8_t*)mColormap) + size, 0, mColormapSize - size);
        }
        if (len < size) {
          // Use 'hold' pattern to get the image colormap
          GETN(size, gif_image_colormap);
          break;
        }
        // Copy everything, go to colormap state to do CMS correction
        memcpy(mColormap, buf, size);
        buf += size;
        len -= size;
        GETN(0, gif_image_colormap);
        break;
      } else {
        /* Switch back to the global palette */
        if (mGIFStruct.images_decoded) {
          // Copy global colormap into the palette of current frame
          memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
        } else {
          mColormap = mGIFStruct.global_colormap;
        }
      }
      GETN(1, gif_lzw_start);
    }
    break;

    case gif_image_colormap:
      // Everything is already copied into local_colormap
      // Convert into Cairo colors including CMS transformation
      ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
      GETN(1, gif_lzw_start);
      break;

    case gif_sub_block:
      mGIFStruct.count = *q;
      if (mGIFStruct.count) {
        /* Still working on the same image: Process next LZW data block */
        /* Make sure there are still rows left. If the GIF data */
        /* is corrupt, we may not get an explicit terminator.   */
        if (!mGIFStruct.rows_remaining) {
#ifdef DONT_TOLERATE_BROKEN_GIFS
          mGIFStruct.state = gif_error;
          break;
#else
          /* This is an illegal GIF, but we remain tolerant. */
          GETN(1, gif_sub_block);
#endif
          if (mGIFStruct.count == GIF_TRAILER) {
            /* Found a terminator anyway, so consider the image done */
            GETN(1, gif_done);
            break;
          }
        }
        GETN(mGIFStruct.count, gif_lzw);
      } else {
        /* See if there are any more images in this sequence. */
        EndImageFrame();
        GETN(1, gif_image_start);
      }
      break;

    case gif_done:
      MOZ_ASSERT(!IsSizeDecode(), "Size decodes shouldn't reach gif_done");
      FinishInternal();
      goto done;

    case gif_error:
      PostDataError();
      return;

    // We shouldn't ever get here.
    default:
      break;
    }
  }

  // if an error state is set but no data remains, code flow reaches here
  if (mGIFStruct.state == gif_error) {
      PostDataError();
      return;
  }

  // Copy the leftover into mGIFStruct.hold
  if (len) {
    // Add what we have sofar to the block
    if (mGIFStruct.state != gif_global_colormap && mGIFStruct.state != gif_image_colormap) {
      if (!SetHold(buf, len)) {
        PostDataError();
        return;
      }
    } else {
      uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap :
                                                               (uint8_t*)mColormap;
      memcpy(p, buf, len);
      mGIFStruct.bytes_in_hold = len;
    }

    mGIFStruct.bytes_to_consume -= len;
  }

// We want to flush before returning if we're on the first frame
done:
  if (!mGIFStruct.images_decoded) {
    FlushImageData();
    mLastFlushedRow = mCurrentRow;
    mLastFlushedPass = mCurrentPass;
  }

  return;
}
Esempio n. 10
0
nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
{
  if (!buf || !len)
    return NS_ERROR_FAILURE;

  const PRUint8 *q = buf;

  // Add what we have sofar to the block
  // If previous call to me left something in the hold first complete current block
  // Or if we are filling the colormaps, first complete the colormap
  PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
               (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
               (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull;
  if (p) {
    // Add what we have sofar to the block
    PRUint32 l = PR_MIN(len, mGIFStruct.bytes_to_consume);
    memcpy(p+mGIFStruct.bytes_in_hold, buf, l);

    if (l < mGIFStruct.bytes_to_consume) {
      // Not enough in 'buf' to complete current block, get more
      mGIFStruct.bytes_in_hold += l;
      mGIFStruct.bytes_to_consume -= l;
      return NS_OK;
    }
    // Reset hold buffer count
    mGIFStruct.bytes_in_hold = 0;
    // Point 'q' to complete block in hold (or in colormap)
    q = p;
  }

  // Invariant:
  //    'q' is start of current to be processed block (hold, colormap or buf)
  //    'bytes_to_consume' is number of bytes to consume from 'buf'
  //    'buf' points to the bytes to be consumed from the input buffer
  //    'len' is number of bytes left in input buffer from position 'buf'.
  //    At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
  //    to point to next buffer, 'len' is adjusted accordingly.
  //    So that next round in for loop, q gets pointed to the next buffer.

  for (;len >= mGIFStruct.bytes_to_consume; q=buf) {
    // Eat the current block from the buffer, q keeps pointed at current block
    buf += mGIFStruct.bytes_to_consume;
    len -= mGIFStruct.bytes_to_consume;

    switch (mGIFStruct.state)
    {
    case gif_lzw:
      if (!DoLzw(q)) {
        mGIFStruct.state = gif_error;
        break;
      }
      GETN(1, gif_sub_block);
      break;

    case gif_lzw_start:
    {
      // Make sure the transparent pixel is transparent in the colormap
      if (mGIFStruct.is_transparent) {
        // Save old value so we can restore it later
        if (mColormap == mGIFStruct.global_colormap)
            mOldColor = mColormap[mGIFStruct.tpixel];
        mColormap[mGIFStruct.tpixel] = 0;
      }

      /* Initialize LZW parser/decoder */
      mGIFStruct.datasize = *q;
      const int clear_code = ClearCode();
      if (mGIFStruct.datasize > MAX_LZW_BITS ||
          clear_code >= MAX_BITS) {
        mGIFStruct.state = gif_error;
        break;
      }

      mGIFStruct.avail = clear_code + 2;
      mGIFStruct.oldcode = -1;
      mGIFStruct.codesize = mGIFStruct.datasize + 1;
      mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
      mGIFStruct.datum = mGIFStruct.bits = 0;

      /* init the tables */
      for (int i = 0; i < clear_code; i++)
        mGIFStruct.suffix[i] = i;

      mGIFStruct.stackp = mGIFStruct.stack;

      GETN(1, gif_sub_block);
    }
    break;

    /* All GIF files begin with "GIF87a" or "GIF89a" */
    case gif_type:
      if (!strncmp((char*)q, "GIF89a", 6)) {
        mGIFStruct.version = 89;
      } else if (!strncmp((char*)q, "GIF87a", 6)) {
        mGIFStruct.version = 87;
      } else {
        mGIFStruct.state = gif_error;
        break;
      }
      GETN(7, gif_global_header);
      break;

    case gif_global_header:
      /* This is the height and width of the "screen" or
       * frame into which images are rendered.  The
       * individual images can be smaller than the
       * screen size and located with an origin anywhere
       * within the screen.
       */

      mGIFStruct.screen_width = GETINT16(q);
      mGIFStruct.screen_height = GETINT16(q + 2);
      mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;

      // screen_bgcolor is not used
      //mGIFStruct.screen_bgcolor = q[5];
      // q[6] = Pixel Aspect Ratio
      //   Not used
      //   float aspect = (float)((q[6] + 15) / 64.0);

      if (q[4] & 0x80) { /* global map */
        // Get the global colormap
        const PRUint32 size = (3 << mGIFStruct.global_colormap_depth);
        if (len < size) {
          // Use 'hold' pattern to get the global colormap
          GETN(size, gif_global_colormap);
          break;
        }
        // Copy everything, go to colormap state to do CMS correction
        memcpy(mGIFStruct.global_colormap, buf, size);
        buf += size;
        len -= size;
        GETN(0, gif_global_colormap);
        break;
      }

      GETN(1, gif_image_start);
      break;

    case gif_global_colormap:
      // Everything is already copied into global_colormap
      // Convert into Cairo colors including CMS transformation
      ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth);
      GETN(1, gif_image_start);
      break;

    case gif_image_start:
      switch (*q) {
        case GIF_TRAILER:
          mGIFStruct.state = gif_done;
          break;

        case GIF_EXTENSION_INTRODUCER:
          GETN(2, gif_extension);
          break;

        case GIF_IMAGE_SEPARATOR:
          GETN(9, gif_image_header);
          break;

        default:
          /* If we get anything other than GIF_IMAGE_SEPARATOR, 
           * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
           * between blocks. The GIF87a spec tells us to keep reading
           * until we find an image separator, but GIF89a says such
           * a file is corrupt. We follow GIF89a and bail out. */
          if (mGIFStruct.images_decoded > 0) {
            /* The file is corrupt, but one or more images have
             * been decoded correctly. In this case, we proceed
             * as if the file were correctly terminated and set
             * the state to gif_done, so the GIF will display.
             */
            mGIFStruct.state = gif_done;
          } else {
            /* No images decoded, there is nothing to display. */
            mGIFStruct.state = gif_error;
          }
      }
      break;

    case gif_extension:
      mGIFStruct.bytes_to_consume = q[1];
      if (mGIFStruct.bytes_to_consume) {
        switch (*q) {
        case GIF_GRAPHIC_CONTROL_LABEL:
          mGIFStruct.state = gif_control_extension;
          break;
  
        case GIF_APPLICATION_EXTENSION_LABEL:
          mGIFStruct.state = gif_application_extension;
          break;
  
        case GIF_COMMENT_LABEL:
          mGIFStruct.state = gif_consume_comment;
          break;
  
        default:
          mGIFStruct.state = gif_skip_block;
        }
      } else {
        GETN(1, gif_image_start);
      }
      break;

    case gif_consume_block:
      if (!*q)
        GETN(1, gif_image_start);
      else
        GETN(*q, gif_skip_block);
      break;

    case gif_skip_block:
      GETN(1, gif_consume_block);
      break;

    case gif_control_extension:
      mGIFStruct.is_transparent = *q & 0x1;
      mGIFStruct.tpixel = q[3];
      mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
      // Some specs say 3rd bit (value 4), other specs say value 3
      // Let's choose 3 (the more popular)
      if (mGIFStruct.disposal_method == 4)
        mGIFStruct.disposal_method = 3;
      mGIFStruct.delay_time = GETINT16(q + 1) * 10;
      GETN(1, gif_consume_block);
      break;

    case gif_comment_extension:
      if (*q)
        GETN(*q, gif_consume_comment);
      else
        GETN(1, gif_image_start);
      break;

    case gif_consume_comment:
      GETN(1, gif_comment_extension);
      break;

    case gif_application_extension:
      /* Check for netscape application extension */
      if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
        !strncmp((char*)q, "ANIMEXTS1.0", 11))
        GETN(1, gif_netscape_extension_block);
      else
        GETN(1, gif_consume_block);
      break;

    /* Netscape-specific GIF extension: animation looping */
    case gif_netscape_extension_block:
      if (*q)
        GETN(*q, gif_consume_netscape_extension);
      else
        GETN(1, gif_image_start);
      break;

    /* Parse netscape-specific application extensions */
    case gif_consume_netscape_extension:
      switch (q[0] & 7) {
        case 1:
          /* Loop entire animation specified # of times.  Only read the
             loop count during the first iteration. */
          mGIFStruct.loop_count = GETINT16(q + 1);
  
          /* Zero loop count is infinite animation loop request */
          if (mGIFStruct.loop_count == 0)
            mGIFStruct.loop_count = -1;
  
          GETN(1, gif_netscape_extension_block);
          break;
        
        case 2:
          /* Wait for specified # of bytes to enter buffer */
          // Don't do this, this extension doesn't exist (isn't used at all) 
          // and doesn't do anything, as our streaming/buffering takes care of it all...
          // See: http://semmix.pl/color/exgraf/eeg24.htm
          GETN(1, gif_netscape_extension_block);
          break;
  
        default:
          // 0,3-7 are yet to be defined netscape extension codes
          mGIFStruct.state = gif_error;
      }
      break;

    case gif_image_header:
    {
      /* Get image offsets, with respect to the screen origin */
      mGIFStruct.x_offset = GETINT16(q);
      mGIFStruct.y_offset = GETINT16(q + 2);

      /* Get image width and height. */
      mGIFStruct.width  = GETINT16(q + 4);
      mGIFStruct.height = GETINT16(q + 6);

      if (!mGIFStruct.images_decoded) {
        /* Work around broken GIF files where the logical screen
         * size has weird width or height.  We assume that GIF87a
         * files don't contain animations.
         */
        if ((mGIFStruct.screen_height < mGIFStruct.height) ||
            (mGIFStruct.screen_width < mGIFStruct.width) ||
            (mGIFStruct.version == 87)) {
          mGIFStruct.screen_height = mGIFStruct.height;
          mGIFStruct.screen_width = mGIFStruct.width;
          mGIFStruct.x_offset = 0;
          mGIFStruct.y_offset = 0;
        }    
        // Create the image container with the right size.
        BeginGIF();

        // If we were doing header-only, we're done
        if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
          return NS_OK;
      }

      /* Work around more broken GIF files that have zero image
         width or height */
      if (!mGIFStruct.height || !mGIFStruct.width) {
        mGIFStruct.height = mGIFStruct.screen_height;
        mGIFStruct.width = mGIFStruct.screen_width;
        if (!mGIFStruct.height || !mGIFStruct.width) {
          mGIFStruct.state = gif_error;
          break;
        }
      }

      /* Depth of colors is determined by colormap */
      /* (q[8] & 0x80) indicates local colormap */
      /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
      PRUint32 depth = mGIFStruct.global_colormap_depth;
      if (q[8] & 0x80)
        depth = (q[8]&0x07) + 1;
      PRUint32 realDepth = depth;
      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
        realDepth++;
      } 
      // Mask to limit the color values within the colormap
      mColorMask = 0xFF >> (8 - realDepth);
      nsresult rv = BeginImageFrame(realDepth);
      if (NS_FAILED(rv) || !mImageData) {
        mGIFStruct.state = gif_error;
        break;
      }

      if (q[8] & 0x40) {
        mGIFStruct.interlaced = PR_TRUE;
        mGIFStruct.ipass = 1;
      } else {
        mGIFStruct.interlaced = PR_FALSE;
        mGIFStruct.ipass = 0;
      }

      /* Only apply the Haeberli display hack on the first frame */
      mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);

      /* Clear state from last image */
      mGIFStruct.irow = 0;
      mGIFStruct.rows_remaining = mGIFStruct.height;
      mGIFStruct.rowp = mImageData;

      /* bits per pixel is q[8]&0x07 */

      if (q[8] & 0x80) /* has a local colormap? */
      {
        mGIFStruct.local_colormap_size = 1 << depth;
        if (!mGIFStruct.images_decoded) {
          // First frame has local colormap, allocate space for it
          // as the image frame doesn't have its own palette
          mColormapSize = sizeof(PRUint32) << realDepth;
          if (!mGIFStruct.local_colormap) {
            mGIFStruct.local_colormap = (PRUint32*)PR_MALLOC(mColormapSize);
            if (!mGIFStruct.local_colormap) {
              mGIFStruct.state = gif_oom;
              break;
            }
          }
          mColormap = mGIFStruct.local_colormap;
        }
        const PRUint32 size = 3 << depth;
        if (mColormapSize > size) {
          // Clear the notfilled part of the colormap
          memset(((PRUint8*)mColormap) + size, 0, mColormapSize - size);
        }
        if (len < size) {
          // Use 'hold' pattern to get the image colormap
          GETN(size, gif_image_colormap);
          break;
        }
        // Copy everything, go to colormap state to do CMS correction
        memcpy(mColormap, buf, size);
        buf += size;
        len -= size;
        GETN(0, gif_image_colormap);
        break;
      } else {
        /* Switch back to the global palette */
        if (mGIFStruct.images_decoded) {
          // Copy global colormap into the palette of current frame
          memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
        } else {
          mColormap = mGIFStruct.global_colormap;
        }
      }
      GETN(1, gif_lzw_start);
    }
    break;

    case gif_image_colormap:
      // Everything is already copied into local_colormap
      // Convert into Cairo colors including CMS transformation
      ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
      GETN(1, gif_lzw_start);
      break;

    case gif_sub_block:
      mGIFStruct.count = *q;
      if (mGIFStruct.count) {
        /* Still working on the same image: Process next LZW data block */
        /* Make sure there are still rows left. If the GIF data */
        /* is corrupt, we may not get an explicit terminator.   */
        if (!mGIFStruct.rows_remaining) {
#ifdef DONT_TOLERATE_BROKEN_GIFS
          mGIFStruct.state = gif_error;
          break;
#else
          /* This is an illegal GIF, but we remain tolerant. */
          GETN(1, gif_sub_block);
#endif
          if (mGIFStruct.count == GIF_TRAILER) {
            /* Found a terminator anyway, so consider the image done */
            GETN(1, gif_done);
            break;
          }
        }
        GETN(mGIFStruct.count, gif_lzw);
      } else {
        /* See if there are any more images in this sequence. */
        EndImageFrame();
        GETN(1, gif_image_start);
      }
      break;

    case gif_done:
      EndGIF(/* aSuccess = */ PR_TRUE);
      return NS_OK;
      break;

    case gif_error:
      EndGIF(/* aSuccess = */ PR_FALSE);
      return NS_ERROR_FAILURE;
      break;

    // Handle out of memory errors
    case gif_oom:
      return NS_ERROR_OUT_OF_MEMORY;

    // We shouldn't ever get here.
    default:
      break;
    }
  }

  // if an error state is set but no data remains, code flow reaches here
  if (mGIFStruct.state == gif_error) {
      EndGIF(/* aSuccess = */ PR_FALSE);
      return NS_ERROR_FAILURE;
  }
  
  // Copy the leftover into mGIFStruct.hold
  mGIFStruct.bytes_in_hold = len;
  if (len) {
    // Add what we have sofar to the block
    PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
                 (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
                 mGIFStruct.hold;
    memcpy(p, buf, len);
    mGIFStruct.bytes_to_consume -= len;
  }

  return NS_OK;
}
Esempio n. 11
0
// Set coords of Boxes
void Cooder(Node* tree, int n) {
  if(GETN(tree, n).cutline == '-') {
    return;
  }
  else if(GETN(tree, n).cutline == 'H') {
    LEFTC(tree, n).xcoord = GETN(tree, n).xcoord;
    LEFTC(tree, n).ycoord = RIGHTC(tree, n).height + GETN(tree, n).ycoord;
    RIGHTC(tree, n).ycoord = GETN(tree, n).ycoord;
    RIGHTC(tree, n).xcoord = GETN(tree, n).xcoord;
  }
  else {
    RIGHTC(tree, n).ycoord = GETN(tree, n).ycoord;
    RIGHTC(tree, n).xcoord = LEFTC(tree, n).width + GETN(tree, n).xcoord;
    LEFTC(tree, n).ycoord = GETN(tree, n).ycoord;
    LEFTC(tree, n).xcoord = GETN(tree, n).xcoord;
  }

  Cooder(tree, GETN(tree, n).left);
  Cooder(tree, GETN(tree, n).right);
  return;
}
Esempio n. 12
0
void ReRoot(Node* tree, int root, Box* min, int nogo_root) { 
  // printf("LOG: root:%d r:%d l:%d\n", root, GETN(tree, root).left, GETN(tree, root).right);

  int nroot; // will be used to store the next root

  Box root_box = {
    GETN(tree, root).width,
    GETN(tree, root).height
  };

  if(LEFTC(tree, root).cutline == '-') {
    return;
  }

  if(nogo_root == GETN(tree, root).right) { // if we cannot take right means we must take left
    // New root is left
    nroot = GETN(tree, root).left;
    Box lroot_box = {
      LEFTC(tree, root).width,
      LEFTC(tree, root).height
    };

    // Root L - L
    // Change the tree structure
    GETN(tree, root).left = GETN(tree, nroot).right;
    GETN(tree, nroot).right = root;
    //Calculate dimensions for the boxes
    MiniBoxer(tree, root, nroot);
    SmallerBox(&GETN(tree, nroot), min); // Check if smaller box
    ReRoot(tree, nroot, min, root); // 
    // UnRoot
    GETN(tree, nroot).right = GETN(tree, root).left;
    GETN(tree, root).left = nroot;
    GETN(tree, nroot).width = lroot_box.width;
    GETN(tree, nroot).height = lroot_box.height;
    GETN(tree, root).width = root_box.width;
    GETN(tree, root).height = root_box.height;

    // Root L - R
    // Change the tree structure
    GETN(tree, root).left = GETN(tree, nroot).left;
    GETN(tree, nroot).left = root;
    MiniBoxer(tree, root, nroot);
    SmallerBox(&GETN(tree, nroot), min); // Check if smaller box
    ReRoot(tree, nroot, min, root);
    // UnRoot
    GETN(tree, nroot).left = GETN(tree, root).left;
    GETN(tree, root).left = nroot;
    GETN(tree, nroot).width = lroot_box.width;
    GETN(tree, nroot).height = lroot_box.height;
    GETN(tree, root).width = root_box.width;
    GETN(tree, root).height = root_box.height;
    return;
  }

  // Base case on right
  if(RIGHTC(tree, root).cutline == '-') {
    return;
  }

  if(nogo_root == GETN(tree, root).left) { // if we cannot take left means we take right
    // Taking the Right Side
    nroot = GETN(tree, root).right;
    Box rroot_box = {
      RIGHTC(tree, root).width,
      RIGHTC(tree, root).height
    };
    // Root R - L
    // Change the tree structure
    GETN(tree, root).right = GETN(tree, nroot).right;
    GETN(tree, nroot).right = root;
    MiniBoxer(tree, root, nroot);
    SmallerBox(&GETN(tree, nroot), min); // Check if smaller box
    ReRoot(tree, nroot, min, root);
    // UnRoot
    GETN(tree, nroot).right = GETN(tree, root).right;
    GETN(tree, root).right = nroot;
    GETN(tree, nroot).width = rroot_box.width;
    GETN(tree, nroot).height = rroot_box.height;
    GETN(tree, root).width = root_box.width;
    GETN(tree, root).height = root_box.height;

    // Root R - R
    // Change the tree structure
    GETN(tree, root).right = GETN(tree, nroot).left;
    GETN(tree, nroot).left = root;
    // Calc new box size
    MiniBoxer(tree, root, nroot);
    SmallerBox(&GETN(tree, nroot), min); // Check if smaller box
    ReRoot(tree, nroot, min, root);
    // UnRoot
    GETN(tree, nroot).left = GETN(tree, root).right;
    GETN(tree, root).right = nroot;
    GETN(tree, nroot).width = rroot_box.width;
    GETN(tree, nroot).height = rroot_box.height;
    GETN(tree, root).width = root_box.width;
    GETN(tree, root).height = root_box.height;
    return;
  }
}