123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- /* Graphics_record.cpp
- *
- * Copyright (C) 1992-2011,2013,2014,2015,2016,2017 Paul Boersma
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This code is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this work. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "GraphicsP.h"
- #define RECORDING_HEADER_LENGTH 2
- double * _Graphics_check (Graphics me, integer number) {
- static bool messageHasAlreadyBeenShownOnce = false;
- double *result = nullptr;
- double *record = my record;
- integer nrecord = my nrecord;
- if (nrecord == 0) {
- nrecord = 1000;
- try {
- record = Melder_malloc (double, 1 + nrecord);
- } catch (MelderError) {
- if (messageHasAlreadyBeenShownOnce) {
- Melder_clearError ();
- } else {
- messageHasAlreadyBeenShownOnce = true;
- Melder_flushError (U"_Graphics_growRecorder: out of memory.\n"
- U"This message will not show up on future occasions."); // because of loop danger when redrawing
- }
- return nullptr;
- }
- my record = record; my nrecord = nrecord;
- }
- if (nrecord < my irecord + RECORDING_HEADER_LENGTH + number) {
- while (nrecord < my irecord + RECORDING_HEADER_LENGTH + number) nrecord *= 2;
- try {
- record = (double *) Melder_realloc (record, (1 + nrecord) * (integer) sizeof (double));
- } catch (MelderError) {
- if (messageHasAlreadyBeenShownOnce) {
- Melder_clearError ();
- } else {
- messageHasAlreadyBeenShownOnce = true;
- Melder_flushError (U"_Graphics_growRecorder: out of memory.\n"
- U"This message will not show up on future occasions."); // because of loop danger when redrawing
- }
- return nullptr;
- }
- my record = record; my nrecord = nrecord;
- }
- result = my record + my irecord;
- my irecord += number + RECORDING_HEADER_LENGTH;
- return result;
- }
- /***** RECORD AND PLAY *****/
- bool Graphics_startRecording (Graphics me) {
- bool wasRecording = my recording;
- my recording = true;
- return wasRecording;
- }
- bool Graphics_stopRecording (Graphics me) {
- bool wasRecording = my recording;
- my recording = false;
- return wasRecording;
- }
- void Graphics_clearRecording (Graphics me) {
- if (my record) {
- Melder_free (my record);
- my irecord = 0;
- my nrecord = 0;
- }
- }
- void Graphics_play (Graphics me, Graphics thee) {
- double *p = my record, *endp = p + my irecord;
- bool wasRecording = my recording;
- if (! p) return;
- my recording = false; // temporarily, in case me == thee
- while (p < endp) {
- #define get (* ++ p)
- #define iget (integer) (* ++ p)
- #define mget(n) (p += n, p - n)
- #define sget(n) ((char *) (p += n, p - n + 1))
- int opcode = (int) get;
- (void) (integer) get; // ignore number of arguments
- switch (opcode) {
- case SET_VIEWPORT: {
- double x1NDC = get, x2NDC = get, y1NDC = get, y2NDC = get;
- Graphics_setViewport (thee, x1NDC, x2NDC, y1NDC, y2NDC);
- } break;
- case SET_INNER: {
- Graphics_setInner (thee);
- } break;
- case UNSET_INNER: {
- Graphics_unsetInner (thee);
- } break;
- case SET_WINDOW: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_setWindow (thee, x1, x2, y1, y2);
- } break;
- case TEXT: {
- double x = get, y = get;
- integer length = iget;
- char *text_utf8 = sget (length);
- Graphics_text (thee, x, y, Melder_peek8to32 (text_utf8));
- } break;
- case POLYLINE: {
- integer n = iget;
- double *x = mget (n), *y = mget (n);
- Graphics_polyline (thee, n, & x [1], & y [1]);
- } break;
- case LINE: {
- double x1 = get, y1 = get, x2 = get, y2 = get;
- Graphics_line (thee, x1, y1, x2, y2);
- } break;
- case ARROW: {
- double x1 = get, y1 = get, x2 = get, y2 = get;
- Graphics_arrow (thee, x1, y1, x2, y2);
- } break;
- case FILL_AREA: {
- integer n = iget;
- double *x = mget (n), *y = mget (n);
- Graphics_fillArea (thee, n, & x [1], & y [1]);
- } break;
- case FUNCTION: {
- integer n = iget;
- double x1 = get, x2 = get, *y = mget (n);
- Graphics_function (thee, y, 1, n, x1, x2);
- } break;
- case RECTANGLE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_rectangle (thee, x1, x2, y1, y2);
- } break;
- case FILL_RECTANGLE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_fillRectangle (thee, x1, x2, y1, y2);
- } break;
- case CIRCLE: {
- double x = get, y = get, r = get;
- Graphics_circle (thee, x, y, r);
- } break;
- case FILL_CIRCLE: {
- double x = get, y = get, r = get;
- Graphics_fillCircle (thee, x, y, r);
- } break;
- case ARC: {
- double x = get, y = get, r = get, fromAngle = get, toAngle = get;
- Graphics_arc (thee, x, y, r, fromAngle, toAngle);
- } break;
- case ARC_ARROW: {
- double x = get, y = get, r = get, fromAngle = get, toAngle = get;
- int arrowAtStart = (int) iget, arrowAtEnd = (int) iget;
- Graphics_arcArrow (thee, x, y, r, fromAngle, toAngle, arrowAtStart, arrowAtEnd);
- } break;
- case HIGHLIGHT: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_highlight (thee, x1, x2, y1, y2);
- } break;
- case CELL_ARRAY: {
- double x1 = get, x2 = get, y1 = get, y2 = get, minimum = get, maximum = get;
- integer nrow = iget, ncol = iget;
- /*
- * We don't copy all the data into a new matrix.
- * Instead, we create row pointers z [1..nrow] that point directly into the recorded data.
- * This works because the data is a packed array of double, just as Graphics_cellArray expects.
- */
- double **z = Melder_malloc_f (double *, nrow);
- z [0] = p + 1;
- for (integer irow = 1; irow < nrow; irow ++) z [irow] = z [irow - 1] + ncol;
- p += nrow * ncol;
- Graphics_cellArray (thee, z, 0, ncol - 1, x1, x2,
- 0, nrow - 1, y1, y2, minimum, maximum);
- Melder_free (z);
- } break;
- case SET_FONT: {
- Graphics_setFont (thee, (enum kGraphics_font) get);
- } break;
- case SET_FONT_SIZE: {
- Graphics_setFontSize (thee, (int) get);
- } break;
- case SET_FONT_STYLE: {
- Graphics_setFontStyle (thee, (int) get);
- } break;
- case SET_TEXT_ALIGNMENT: {
- kGraphics_horizontalAlignment hor = (kGraphics_horizontalAlignment) iget;
- int vert = (int) iget;
- Graphics_setTextAlignment (thee, hor, vert);
- } break;
- case SET_TEXT_ROTATION: {
- Graphics_setTextRotation (thee, get);
- } break;
- case SET_LINE_TYPE: {
- Graphics_setLineType (thee, (int) get);
- } break;
- case SET_LINE_WIDTH: {
- Graphics_setLineWidth (thee, get);
- } break;
- case SET_STANDARD_COLOUR: { // only used in old Praat picture files
- int standardColour = (int) get;
- Graphics_Colour colour =
- standardColour == 0 ? Graphics_BLACK :
- standardColour == 1 ? Graphics_WHITE :
- standardColour == 2 ? Graphics_RED :
- standardColour == 3 ? Graphics_GREEN :
- standardColour == 4 ? Graphics_BLUE :
- standardColour == 5 ? Graphics_CYAN :
- standardColour == 6 ? Graphics_MAGENTA :
- standardColour == 7 ? Graphics_YELLOW :
- standardColour == 8 ? Graphics_MAROON :
- standardColour == 9 ? Graphics_LIME :
- standardColour == 10 ? Graphics_NAVY :
- standardColour == 11 ? Graphics_TEAL :
- standardColour == 12 ? Graphics_PURPLE :
- standardColour == 13 ? Graphics_OLIVE :
- standardColour == 14 ? Graphics_PINK :
- standardColour == 15 ? Graphics_SILVER :
- Graphics_GREY;
- Graphics_setColour (thee, colour);
- } break;
- case SET_GREY: {
- Graphics_setGrey (thee, get);
- } break;
- case MARK_GROUP: {
- Graphics_markGroup (thee);
- } break;
- case ELLIPSE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_ellipse (thee, x1, x2, y1, y2);
- } break;
- case FILL_ELLIPSE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_fillEllipse (thee, x1, x2, y1, y2);
- } break;
- case CIRCLE_MM: {
- double x = get, y = get, d = get;
- Graphics_circle_mm (thee, x, y, d);
- } break;
- case FILL_CIRCLE_MM: {
- double x = get, y = get, d = get;
- Graphics_fillCircle_mm (thee, x, y, d);
- } break;
- case IMAGE8: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- uint8 minimum = (uint8) iget, maximum = (uint8) iget;
- integer nrow = iget, ncol = iget;
- uint8 **z = NUMmatrix <uint8> (1, nrow, 1, ncol); // BUG memory
- for (integer irow = 1; irow <= nrow; irow ++)
- for (integer icol = 1; icol <= ncol; icol ++)
- z [irow] [icol] = (uint8) iget;
- Graphics_image8 (thee, z, 1, ncol, x1, x2, 1, nrow, y1, y2, minimum, maximum);
- NUMmatrix_free (z, 1, 1);
- } break;
- case UNHIGHLIGHT: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_unhighlight (thee, x1, x2, y1, y2);
- } break;
- #if motif
- case XOR_ON: {
- Graphics_Colour colour; colour. red = get, colour. green = get, colour. blue = get;
- Graphics_xorOn (thee, colour);
- } break;
- case XOR_OFF: {
- Graphics_xorOff (thee);
- } break;
- #endif
- case RECTANGLE_MM: {
- double x = get, y = get, horSide = get, vertSide = get;
- Graphics_rectangle_mm (thee, x, y, horSide, vertSide);
- } break;
- case FILL_RECTANGLE_MM: {
- double x = get, y = get, horSide = get, vertSide = get;
- Graphics_fillRectangle_mm (thee, x, y, horSide, vertSide);
- } break;
- case SET_WS_WINDOW: {
- double x1NDC = get, x2NDC = get, y1NDC = get, y2NDC = get;
- Graphics_setWsWindow (thee, x1NDC, x2NDC, y1NDC, y2NDC);
- } break;
- case SET_WRAP_WIDTH: {
- Graphics_setWrapWidth (thee, get);
- } break;
- case SET_SECOND_INDENT: {
- Graphics_setSecondIndent (thee, get);
- } break;
- case SET_PERCENT_SIGN_IS_ITALIC: {
- Graphics_setPercentSignIsItalic (thee, (bool) get);
- } break;
- case SET_NUMBER_SIGN_IS_BOLD: {
- Graphics_setNumberSignIsBold (thee, (bool) get);
- } break;
- case SET_CIRCUMFLEX_IS_SUPERSCRIPT: {
- Graphics_setCircumflexIsSuperscript (thee, (bool) get);
- } break;
- case SET_UNDERSCORE_IS_SUBSCRIPT: {
- Graphics_setUnderscoreIsSubscript (thee, (bool) get);
- } break;
- case SET_DOLLAR_SIGN_IS_CODE: {
- Graphics_setDollarSignIsCode (thee, (bool) get);
- } break;
- case SET_AT_SIGN_IS_LINK: {
- Graphics_setAtSignIsLink (thee, (bool) get);
- } break;
- case BUTTON: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_button (thee, x1, x2, y1, y2);
- } break;
- case ROUNDED_RECTANGLE: {
- double x1 = get, x2 = get, y1 = get, y2 = get, r = get;
- Graphics_roundedRectangle (thee, x1, x2, y1, y2, r);
- } break;
- case FILL_ROUNDED_RECTANGLE: {
- double x1 = get, x2 = get, y1 = get, y2 = get, r = get;
- Graphics_fillRoundedRectangle (thee, x1, x2, y1, y2, r);
- } break;
- case FILL_ARC: {
- double x = get, y = get, r = get, fromAngle = get, toAngle = get;
- Graphics_fillArc (thee, x, y, r, fromAngle, toAngle);
- } break;
- case INNER_RECTANGLE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- Graphics_innerRectangle (thee, x1, x2, y1, y2);
- } break;
- case CELL_ARRAY8: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- uint8 minimum = (uint8) iget, maximum = (uint8) iget;
- integer nrow = iget, ncol = iget;
- uint8 **z = NUMmatrix <uint8> (1, nrow, 1, ncol); // BUG memory
- for (integer irow = 1; irow <= nrow; irow ++)
- for (integer icol = 1; icol <= ncol; icol ++)
- z [irow] [icol] = (uint8) iget;
- Graphics_cellArray8 (thee, z, 1, ncol, x1, x2, 1, nrow, y1, y2, minimum, maximum);
- NUMmatrix_free (z, 1, 1);
- } break;
- case IMAGE: {
- double x1 = get, x2 = get, y1 = get, y2 = get, minimum = get, maximum = get;
- integer nrow = iget, ncol = iget;
- /*
- * We don't copy all the data into a new matrix.
- * Instead, we create row pointers z [1..nrow] that point directly into the recorded data.
- * This works because the data is a packed array of double, just as Graphics_image expects.
- */
- double **z = Melder_malloc_f (double *, nrow);
- z [0] = p + 1;
- for (integer irow = 1; irow < nrow; irow ++) z [irow] = z [irow - 1] + ncol;
- p += nrow * ncol;
- Graphics_image (thee, z, 0, ncol - 1, x1, x2,
- 0, nrow - 1, y1, y2, minimum, maximum);
- Melder_free (z);
- } break;
- case HIGHLIGHT2: {
- double x1 = get, x2 = get, y1 = get, y2 = get, innerX1 = get, innerX2 = get, innerY1 = get, innerY2 = get;
- Graphics_highlight2 (thee, x1, x2, y1, y2, innerX1, innerX2, innerY1, innerY2);
- } break;
- case UNHIGHLIGHT2: {
- double x1 = get, x2 = get, y1 = get, y2 = get, innerX1 = get, innerX2 = get, innerY1 = get, innerY2 = get;
- Graphics_unhighlight2 (thee, x1, x2, y1, y2, innerX1, innerX2, innerY1, innerY2);
- } break;
- case SET_ARROW_SIZE: {
- Graphics_setArrowSize (thee, get);
- } break;
- case DOUBLE_ARROW: {
- double x1 = get, y1 = get, x2 = get, y2 = get;
- Graphics_doubleArrow (thee, x1, y1, x2, y2);
- } break;
- case SET_RGB_COLOUR: {
- Graphics_Colour colour;
- colour. red = get, colour. green = get, colour. blue = get;
- Graphics_setColour (thee, colour);
- } break;
- case IMAGE_FROM_FILE: {
- double x1 = get, x2 = get, y1 = get, y2 = get;
- integer length = iget;
- char *text_utf8 = sget (length);
- Graphics_imageFromFile (thee, Melder_peek8to32 (text_utf8), x1, x2, y1, y2);
- } break;
- case POLYLINE_CLOSED: {
- integer n = iget;
- double *x = mget (n), *y = mget (n);
- Graphics_polyline_closed (thee, n, & x [1], & y [1]);
- } break;
- case CELL_ARRAY_COLOUR: {
- double x1 = get, x2 = get, y1 = get, y2 = get, minimum = get, maximum = get;
- integer nrow = iget, ncol = iget;
- /*
- * We don't copy all the data into a new matrix.
- * Instead, we create row pointers z [1..nrow] that point directly into the recorded data.
- * This works because the data is a packed array of double_rgbt, just as Graphics_cellArray_colour expects.
- */
- double_rgbt **z = Melder_malloc_f (double_rgbt *, nrow);
- z [0] = (double_rgbt *) (p + 1);
- for (integer irow = 1; irow < nrow; irow ++) z [irow] = z [irow - 1] + ncol;
- p += nrow * ncol * 4;
- Graphics_cellArray_colour (thee, z, 0, ncol - 1, x1, x2,
- 0, nrow - 1, y1, y2, minimum, maximum);
- Melder_free (z);
- } break;
- case IMAGE_COLOUR: {
- double x1 = get, x2 = get, y1 = get, y2 = get, minimum = get, maximum = get;
- integer nrow = iget, ncol = iget;
- /*
- * We don't copy all the data into a new matrix.
- * Instead, we create row pointers z [1..nrow] that point directly into the recorded data.
- * This works because the data is a packed array of double_rgbt, just as Graphics_image_colour expects.
- */
- double_rgbt **z = Melder_malloc_f (double_rgbt *, nrow);
- z [0] = (double_rgbt *) (p + 1);
- for (integer irow = 1; irow < nrow; irow ++) z [irow] = z [irow - 1] + ncol;
- p += nrow * ncol * 4;
- Graphics_image_colour (thee, z, 0, ncol - 1, x1, x2,
- 0, nrow - 1, y1, y2, minimum, maximum);
- Melder_free (z);
- } break;
- case SET_COLOUR_SCALE: {
- Graphics_setColourScale (thee, (enum kGraphics_colourScale) get);
- } break;
- case SET_SPECKLE_SIZE: {
- Graphics_setSpeckleSize (thee, get);
- } break;
- case SPECKLE: {
- double x = get, y = get;
- Graphics_speckle (thee, x, y);
- } break;
- default:
- my recording = wasRecording;
- Melder_flushError (U"Graphics_play: unknown opcode (", opcode, U").\n", p [-1], U" ", p [1]);
- return;
- }
- }
- my recording = wasRecording;
- }
- void Graphics_writeRecordings (Graphics me, FILE *f) {
- double *p = my record, *endp = p + my irecord;
- if (! p) return;
- binputi32 (my irecord, f);
- while (p < endp) {
- #define get (* ++ p)
- int opcode = (int) get;
- binputr32 ((float) opcode, f);
- integer numberOfArguments = (integer) get;
- const integer largestIntegerRepresentableAs32BitFloat = 0x00FFFFFF;
- if (numberOfArguments > largestIntegerRepresentableAs32BitFloat) {
- binputr32 (-1.0, f);
- binputi32 (numberOfArguments, f);
- //Melder_warning ("This picture is very large!");
- } else {
- binputr32 ((float) numberOfArguments, f);
- }
- if (opcode == TEXT) {
- binputr32 (get, f); // x
- binputr32 (get, f); // y
- binputr32 (get, f); // length
- Melder_assert (sizeof (double) == 8);
- if ((integer) fwrite (++ p, 8, numberOfArguments - 3, f) < numberOfArguments - 3) // text
- Melder_throw (U"Error writing graphics recordings.");
- p += numberOfArguments - 4;
- } else if (opcode == IMAGE_FROM_FILE) {
- binputr32 (get, f); // x1
- binputr32 (get, f); // x2
- binputr32 (get, f); // y1
- binputr32 (get, f); // y2
- binputr32 (get, f); // length
- Melder_assert (sizeof (double) == 8);
- if ((integer) fwrite (++ p, 8, numberOfArguments - 5, f) < numberOfArguments - 5) // text
- Melder_throw (U"Error writing graphics recordings.");
- p += numberOfArguments - 6;
- } else {
- for (integer i = numberOfArguments; i > 0; i --) binputr32 (get, f);
- }
- }
- }
- void Graphics_readRecordings (Graphics me, FILE *f) {
- integer old_irecord = my irecord;
- integer added_irecord = 0;
- double* p = nullptr;
- double* endp = nullptr;
- integer numberOfArguments = 0;
- int opcode = 0;
- try {
- added_irecord = bingeti32 (f);
- p = _Graphics_check (me, added_irecord - RECORDING_HEADER_LENGTH);
- if (! p) return;
- Melder_assert (my irecord == old_irecord + added_irecord);
- endp = p + added_irecord;
- while (p < endp) {
- opcode = (int) bingetr32 (f);
- put (opcode);
- numberOfArguments = (integer) bingetr32 (f);
- if (numberOfArguments == -1)
- numberOfArguments = bingeti32 (f);
- put (numberOfArguments);
- if (opcode == TEXT) {
- put (bingetr32 (f)); // x
- put (bingetr32 (f)); // y
- put (bingetr32 (f)); // length
- if (fread (++ p, 8, (size_t) numberOfArguments - 3, f) < (size_t) numberOfArguments - 3) // text
- Melder_throw (U"Error reading graphics recordings.");
- p += numberOfArguments - 4;
- } else if (opcode == IMAGE_FROM_FILE) {
- put (bingetr32 (f)); // x1
- put (bingetr32 (f)); // x2
- put (bingetr32 (f)); // y1
- put (bingetr32 (f)); // y2
- put (bingetr32 (f)); // length
- if (fread (++ p, 8, (size_t) numberOfArguments - 5, f) < (size_t) numberOfArguments - 5) // text
- Melder_throw (U"Error reading graphics recordings.");
- p += numberOfArguments - 6;
- } else {
- for (integer i = numberOfArguments; i > 0; i --)
- put (bingetr32 (f));
- }
- }
- } catch (MelderError) {
- my irecord = old_irecord;
- Melder_throw (U"Error reading graphics record ", added_irecord - (integer) (endp - p),
- U" out of ", added_irecord, U".\nOpcode ", opcode, U", args ", numberOfArguments, U".");
- }
- }
- void Graphics_markGroup (Graphics me) {
- if (my recording) { op (MARK_GROUP, 0); }
- }
- void Graphics_undoGroup (Graphics me) {
- integer lastMark = 0; // not yet found
- integer jrecord = 0;
- while (jrecord < my irecord) { // keep looking for marks until the end
- int opcode = (int) my record [++ jrecord];
- integer number = (integer) my record [++ jrecord];
- if (opcode == MARK_GROUP) lastMark = jrecord - 1; // found a mark
- jrecord += number;
- }
- if (jrecord != my irecord) Melder_flushError (U"jrecord != my irecord: ", jrecord, U", ", my irecord);
- if (lastMark > 0) // found?
- my irecord = lastMark - 1; // forget all graphics from and including the last mark
- }
- /* End of file Graphics_record.cpp */
|