summaryrefslogtreecommitdiff
path: root/java/org/gnu/emacs/EmacsGC.java
blob: 0b0f09c7ab9f8365f8bb8af933174380c8cd0500 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-

Copyright (C) 2023-2024 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs 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 3 of the License, or (at
your option) any later version.

GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */

package org.gnu.emacs;

import android.graphics.Rect;
import android.graphics.Paint;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Shader.TileMode;

import android.os.Build;

/* X like graphics context structures.  Keep the enums in synch with
   androidgui.h! */

public final class EmacsGC extends EmacsHandleObject
{
  public static final int GC_COPY    = 0;
  public static final int GC_INVERT  = 1;

  public static final int GC_FILL_SOLID			= 0;
  public static final int GC_FILL_OPAQUE_STIPPLED	= 1;

  public static final int GC_LINE_SOLID			= 0;
  public static final int GC_LINE_ON_OFF_DASH		= 1;

  public int function, fill_style;
  public int foreground, background;
  public int clip_x_origin, clip_y_origin;
  public int ts_origin_x, ts_origin_y;
  public int line_style, line_width;
  public int dashes[], dash_offset;
  public Rect clip_rects[], real_clip_rects[];
  public EmacsPixmap clip_mask, stipple;
  public Paint gcPaint;

  /* Drawable object for rendering the stipple bitmap.  */
  public EmacsTileObject tileObject;

  /* ID incremented every time the clipping rectangles of any GC
     changes.  */
  private static long clip_serial;

  /* The value of clipRectID after the last time this GCs clip
     rectangles changed.  0 if there are no clip rectangles.  */
  public long clipRectID;

  /* The following fields are only set on immutable GCs.  */

  public
  EmacsGC ()
  {
    /* For historical reasons the C code has an extra layer of
       indirection above this GC handle.  struct android_gc is the GC
       used by Emacs code, while android_gcontext is the type of the
       handle.  */
    super ();

    fill_style = GC_FILL_SOLID;
    function = GC_COPY;
    foreground = 0;
    background = 0xffffff;
    gcPaint = new Paint ();

    /* Android S and above enable anti-aliasing unless explicitly told
       otherwise.  */
    gcPaint.setAntiAlias (false);
  }

  /* Mark this GC as dirty.  Apply parameters to the paint and
     recompute real_clip_rects.  */

  public void
  markDirty (boolean clipRectsChanged)
  {
    int i;
    Bitmap stippleBitmap;

    if (clipRectsChanged)
      {
	if ((ts_origin_x != 0 || ts_origin_y != 0)
	    && clip_rects != null)
	  {
	    real_clip_rects = new Rect[clip_rects.length];

	    for (i = 0; i < clip_rects.length; ++i)
	      {
		real_clip_rects[i] = new Rect (clip_rects[i]);
		real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
	      }
	  }
	else
	  real_clip_rects = clip_rects;

	clipRectID = ++clip_serial;
      }

    /* A line_width of 0 is equivalent to that of 1.  */
    gcPaint.setStrokeWidth (line_width < 1 ? 1 : line_width);
    gcPaint.setColor (foreground | 0xff000000);

    /* Update the stipple object with the new stipple bitmap, or delete
       it if the stipple has been cleared on systems too old to support
       modifying such objects.  */

    if (stipple != null)
      {
	stippleBitmap = stipple.getBitmap ();

	/* Allocate a new tile object if none is already present or it
	   cannot be reconfigured.  */
	if (tileObject == null)
	  {
	    tileObject = new EmacsTileObject (stippleBitmap);
	    tileObject.setTileModeXY (TileMode.REPEAT, TileMode.REPEAT);
	  }
	else
	  /* Otherwise, update the existing tile object with the new
	     bitmap.  */
	  tileObject.setBitmap (stippleBitmap);
      }
    else if (tileObject != null)
      tileObject.setBitmap (null);
  }

  /* Prepare the tile object to draw a stippled image onto a section of
     a drawable defined by RECT.  It is an error to call this function
     unless the `stipple' field of the GContext is set.  */

  private void
  prepareStipple (Rect rect)
  {
    int sx, sy; /* Stipple origin.  */
    int bw, bh; /* Stipple size.  */
    Bitmap bitmap;
    Rect boundsRect;

    /* Retrieve the dimensions of the stipple bitmap, which doubles as
       the unit of advance for this stipple.  */
    bitmap = tileObject.getBitmap ();
    bw     = bitmap.getWidth ();
    bh     = bitmap.getHeight ();

    /* Align the lower left corner of the bounds rectangle to the
       initial position of the stipple.  */
    sx = (rect.left % bw) * -1 + (-ts_origin_x % bw) * -1;
    sy = (rect.top  % bh) * -1 + (-ts_origin_y % bh) * -1;
    boundsRect = new Rect (rect.left + sx, rect.top + sy,
			   rect.right, rect.bottom);
    tileObject.setBounds (boundsRect);
  }

  /* Fill the rectangle BOUNDS in the provided CANVAS with the stipple
     pattern defined for this GContext, in the foreground color where
     the pattern is on, and in the background color where off.  */

  protected void
  blitOpaqueStipple (Canvas canvas, Rect rect)
  {
    ColorFilter filter;

    prepareStipple (rect);
    filter = new PorterDuffColorFilter (foreground | 0xff000000,
					Mode.SRC_IN);
    tileObject.setColorFilter (filter);

    canvas.save ();
    canvas.clipRect (rect);

    tileObject.draw (canvas);
    filter = new PorterDuffColorFilter (background | 0xff000000,
					Mode.SRC_OUT);
    tileObject.setColorFilter (filter);
    tileObject.draw (canvas);
    canvas.restore ();
  }
};